# HG changeset patch # User maiku@pidgin.im # Date 1257908841 0 # Node ID e4884dbf0c0249f7bba5f520a0fa483293b16173 # Parent f93ac891ff0108cb10dc725a13ee8abdc50bf5bd# Parent 1eb68d854dfc77069de96ebf5e4705d1fad25af2 propagate from branch 'im.pidgin.cpw.maiku.media_refactor' (head 69be24ee75b8f957cfee3cfb7fd71721078b7f81) to branch 'im.pidgin.pidgin.next.minor' (head 25b1417fdd880468e1d6f027f8f325b4c56e9f02) diff -r f93ac891ff01 -r e4884dbf0c02 ChangeLog.API --- a/ChangeLog.API Wed Nov 11 02:47:50 2009 +0000 +++ b/ChangeLog.API Wed Nov 11 03:07:21 2009 +0000 @@ -4,6 +4,10 @@ libpurple: Added: * purple_account_get_name_for_display + * purple_media_candidate_copy + * purple_media_codec_copy + * purple_media_manager_get_backend_type + * purple_media_manager_set_backend_type * purple_network_get_all_local_system_ips * purple_prpl_got_media_caps * purple_uuid_random diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/Makefile.am --- a/libpurple/Makefile.am Wed Nov 11 02:47:50 2009 +0000 +++ b/libpurple/Makefile.am Wed Nov 11 03:07:21 2009 +0000 @@ -53,7 +53,12 @@ idle.c \ imgstore.c \ log.c \ - media.c \ + media/backend-fs2.c \ + media/backend-iface.c \ + media/candidate.c \ + media/codec.c \ + media/enum-types.c \ + media/media.c \ mediamanager.c \ mime.c \ nat-pmp.c \ @@ -156,6 +161,12 @@ xmlnode.h \ whiteboard.h +purple_mediaheaders = \ + backend-iface.h \ + candidate.h \ + codec.h \ + enum-types.h + purple_builtheaders = purple.h version.h marshallers.h marshallers.h: marshallers.list @@ -192,6 +203,7 @@ savedstatuses.h smiley.h status.h server.h util.h xmlnode.h prpl.h purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) \ + $(addprefix $(srcdir)/media/, $(purple_mediaheaders)) \ $(purple_builtheaders) dbus_build_exported = $(addprefix $(srcdir)/, $(dbus_exported)) # We should probably make this better @@ -226,7 +238,7 @@ purple-client-bindings.c: dbus-analyze-functions.py $(dbus_exported) cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client > $@ -purple-client-bindings.h: dbus-analyze-types.py dbus-analyze-functions.py $(purple_coreheaders) $(purple_builtheaders) $(dbus_exported) +purple-client-bindings.h: dbus-analyze-types.py dbus-analyze-functions.py $(purple_coreheaders) $(addprefix media/, $(purple_mediaheaders)) $(purple_builtheaders) $(dbus_exported) cat $(purple_build_coreheaders) | $(PYTHON) $(srcdir)/dbus-analyze-types.py --keyword=enum --verbatim > $@ cat $(dbus_build_exported) | $(PYTHON) $(srcdir)/dbus-analyze-functions.py --client --headers >> $@ @@ -275,6 +287,7 @@ noinst_HEADERS= \ internal.h \ + media/backend-fs2.h \ valgrind.h libpurpleincludedir=$(includedir)/libpurple @@ -283,6 +296,10 @@ $(purple_builtheaders) \ $(dbus_headers) +mediaincludedir=$(includedir)/libpurple/media +mediainclude_HEADERS = \ + $(addprefix $(srcdir)/media/, $(purple_mediaheaders)) + libpurple_la_DEPENDENCIES = $(STATIC_LINK_LIBS) libpurple_la_LDFLAGS = -export-dynamic -version-info $(PURPLE_LT_VERSION_INFO) -no-undefined libpurple_la_LIBADD = \ diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/Makefile.mingw --- a/libpurple/Makefile.mingw Wed Nov 11 02:47:50 2009 +0000 +++ b/libpurple/Makefile.mingw Wed Nov 11 03:07:21 2009 +0000 @@ -48,7 +48,12 @@ idle.c \ imgstore.c \ log.c \ - media.c \ + media/backend-fs2.c \ + media/backend-iface.c \ + media/candidate.c \ + media/codec.c \ + media/enum-types.c \ + media/media.c \ mediamanager.c \ mime.c \ nat-pmp.c \ diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/dnsquery.c diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/dnssrv.c diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/marshallers.list --- a/libpurple/marshallers.list Wed Nov 11 02:47:50 2009 +0000 +++ b/libpurple/marshallers.list Wed Nov 11 03:07:21 2009 +0000 @@ -5,3 +5,4 @@ VOID:ENUM,STRING,STRING VOID:ENUM,STRING,STRING,BOOLEAN VOID:FLAGS,FLAGS +VOID:STRING,STRING,OBJECT,OBJECT diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media.c --- a/libpurple/media.c Wed Nov 11 02:47:50 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3358 +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 - -#include "internal.h" - -#include "account.h" -#include "media.h" -#include "mediamanager.h" -#include "network.h" - -#include "debug.h" - -#ifdef USE_GSTREAMER -#include "marshallers.h" -#include "media-gst.h" -#endif - -#ifdef USE_VV - -#include -#include - -/** @copydoc _PurpleMediaSession */ -typedef struct _PurpleMediaSession PurpleMediaSession; -/** @copydoc _PurpleMediaStream */ -typedef struct _PurpleMediaStream PurpleMediaStream; -/** @copydoc _PurpleMediaClass */ -typedef struct _PurpleMediaClass PurpleMediaClass; -/** @copydoc _PurpleMediaPrivate */ -typedef struct _PurpleMediaPrivate PurpleMediaPrivate; -/** @copydoc _PurpleMediaCandidateClass */ -typedef struct _PurpleMediaCandidateClass PurpleMediaCandidateClass; -/** @copydoc _PurpleMediaCandidatePrivate */ -typedef struct _PurpleMediaCandidatePrivate PurpleMediaCandidatePrivate; -/** @copydoc _PurpleMediaCodecClass */ -typedef struct _PurpleMediaCodecClass PurpleMediaCodecClass; -/** @copydoc _PurpleMediaCodecPrivate */ -typedef struct _PurpleMediaCodecPrivate PurpleMediaCodecPrivate; - -/** 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; - GstElement *src; - GstElement *tee; - FsSession *session; - - PurpleMediaSessionType type; - gboolean initiator; -}; - -struct _PurpleMediaStream -{ - PurpleMediaSession *session; - gchar *participant; - FsStream *stream; - GstElement *src; - GstElement *tee; - GstElement *volume; - GstElement *level; - - GList *local_candidates; - GList *remote_candidates; - - gboolean initiator; - gboolean accepted; - gboolean candidates_prepared; - gboolean held; - gboolean paused; - - GList *active_local_candidates; - GList *active_remote_candidates; - - guint connected_cb_id; -}; -#endif - -struct _PurpleMediaPrivate -{ -#ifdef USE_VV - PurpleMediaManager *manager; - PurpleAccount *account; - FsConference *conference; - gboolean initiator; - gpointer prpl_data; - - GHashTable *sessions; /* PurpleMediaSession table */ - GHashTable *participants; /* FsParticipant table */ - - GList *streams; /* PurpleMediaStream table */ - - GstElement *confbin; -#else - gpointer dummy; -#endif -}; - -#ifdef USE_VV -#define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate)) -#define PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidatePrivate)) -#define PURPLE_MEDIA_CODEC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodecPrivate)) - -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(FsStream *stream, - FsCandidate *local_candidate, PurpleMediaSession *session); -static void purple_media_candidates_prepared_cb(FsStream *stream, - PurpleMediaSession *session); -static void purple_media_candidate_pair_established_cb(FsStream *stream, - FsCandidate *native_candidate, FsCandidate *remote_candidate, - PurpleMediaSession *session); -static gboolean media_bus_call(GstBus *bus, - GstMessage *msg, 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_ACCOUNT, - PROP_CONFERENCE, - PROP_INITIATOR, - PROP_PRPL_DATA, -}; -#endif - - -/* - * PurpleMediaElementType - */ - -GType -purple_media_session_type_get_type() -{ - static GType type = 0; - if (type == 0) { - static const GFlagsValue values[] = { - { PURPLE_MEDIA_NONE, - "PURPLE_MEDIA_NONE", "none" }, - { PURPLE_MEDIA_RECV_AUDIO, - "PURPLE_MEDIA_RECV_AUDIO", "recv-audio" }, - { PURPLE_MEDIA_SEND_AUDIO, - "PURPLE_MEDIA_SEND_AUDIO", "send-audio" }, - { PURPLE_MEDIA_RECV_VIDEO, - "PURPLE_MEDIA_RECV_VIDEO", "recv-video" }, - { PURPLE_MEDIA_SEND_VIDEO, - "PURPLE_MEDIA_SEND_VIDEO", "send-audio" }, - { PURPLE_MEDIA_AUDIO, - "PURPLE_MEDIA_AUDIO", "audio" }, - { PURPLE_MEDIA_VIDEO, - "PURPLE_MEDIA_VIDEO", "video" }, - { 0, NULL, NULL } - }; - type = g_flags_register_static( - "PurpleMediaSessionType", values); - } - return type; -} - -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 -} - -GType -purple_media_state_changed_get_type() -{ - static GType type = 0; - if (type == 0) { - static const GEnumValue values[] = { - { PURPLE_MEDIA_STATE_NEW, - "PURPLE_MEDIA_STATE_NEW", "new" }, - { PURPLE_MEDIA_STATE_CONNECTED, - "PURPLE_MEDIA_STATE_CONNECTED", "connected" }, - { PURPLE_MEDIA_STATE_END, - "PURPLE_MEDIA_STATE_END", "end" }, - { 0, NULL, NULL } - }; - type = g_enum_register_static("PurpleMediaState", values); - } - return type; -} - -GType -purple_media_info_type_get_type() -{ - static GType type = 0; - if (type == 0) { - static const GEnumValue values[] = { - { PURPLE_MEDIA_INFO_HANGUP, - "PURPLE_MEDIA_INFO_HANGUP", "hangup" }, - { PURPLE_MEDIA_INFO_ACCEPT, - "PURPLE_MEDIA_INFO_ACCEPT", "accept" }, - { PURPLE_MEDIA_INFO_REJECT, - "PURPLE_MEDIA_INFO_REJECT", "reject" }, - { PURPLE_MEDIA_INFO_MUTE, - "PURPLE_MEDIA_INFO_MUTE", "mute" }, - { PURPLE_MEDIA_INFO_UNMUTE, - "PURPLE_MEDIA_INFO_UNMUTE", "unmute" }, - { PURPLE_MEDIA_INFO_PAUSE, - "PURPLE_MEDIA_INFO_PAUSE", "pause" }, - { PURPLE_MEDIA_INFO_UNPAUSE, - "PURPLE_MEDIA_INFO_UNPAUSE", "unpause" }, - { PURPLE_MEDIA_INFO_HOLD, - "PURPLE_MEDIA_INFO_HOLD", "hold" }, - { PURPLE_MEDIA_INFO_UNHOLD, - "PURPLE_MEDIA_INFO_UNHOLD", "unhold" }, - { 0, NULL, NULL } - }; - type = g_enum_register_static("PurpleMediaInfoType", values); - } - return type; -} - -GType -purple_media_caps_get_type() -{ - static GType type = 0; - if (type == 0) { - static const GEnumValue values[] = { - { PURPLE_MEDIA_CAPS_NONE, - "PURPLE_MEDIA_CAPS_NONE", "none" }, - { PURPLE_MEDIA_CAPS_AUDIO, - "PURPLE_MEDIA_CAPS_AUDIO", "audio" }, - { PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION, - "PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION", - "audio-single-direction" }, - { PURPLE_MEDIA_CAPS_VIDEO, - "PURPLE_MEDIA_CAPS_VIDEO", "video" }, - { PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION, - "PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION", - "video-single-direction" }, - { PURPLE_MEDIA_CAPS_AUDIO_VIDEO, - "PURPLE_MEDIA_CAPS_AUDIO_VIDEO", - "audio-video" }, - { PURPLE_MEDIA_CAPS_MODIFY_SESSION, - "PURPLE_MEDIA_CAPS_MODIFY_SESSION", - "modify-session" }, - { PURPLE_MEDIA_CAPS_CHANGE_DIRECTION, - "PURPLE_MEDIA_CAPS_CHANGE_DIRECTION", - "change-direction" }, - { 0, NULL, NULL } - }; - type = g_enum_register_static("PurpleMediaCaps", values); - } - return type; -} - -#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)); - - 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, - g_param_spec_object("conference", - "Farsight conference", - "The FsConference associated with this media.", - FS_TYPE_CONFERENCE, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); - - 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; - - /* Remove the connected_cb timeout */ - if (stream->connected_cb_id != 0) - purple_timeout_remove(stream->connected_cb_id); - - g_free(stream->participant); - - if (stream->local_candidates) - fs_candidate_list_destroy(stream->local_candidates); - if (stream->remote_candidates) - fs_candidate_list_destroy(stream->remote_candidates); - - if (stream->active_local_candidates) - fs_candidate_list_destroy(stream->active_local_candidates); - if (stream->active_remote_candidates) - fs_candidate_list_destroy(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); - GList *iter = NULL; - - purple_debug_info("media","purple_media_dispose\n"); - - purple_media_manager_remove_media(priv->manager, PURPLE_MEDIA(media)); - - if (priv->confbin) { - gst_element_set_locked_state(priv->confbin, TRUE); - gst_element_set_state(GST_ELEMENT(priv->confbin), - GST_STATE_NULL); - gst_bin_remove(GST_BIN(purple_media_manager_get_pipeline( - priv->manager)), priv->confbin); - priv->confbin = NULL; - priv->conference = NULL; - } - - for (iter = priv->streams; iter; iter = g_list_next(iter)) { - PurpleMediaStream *stream = iter->data; - if (stream->stream) { - g_object_unref(stream->stream); - stream->stream = NULL; - } - } - - if (priv->sessions) { - GList *sessions = g_hash_table_get_values(priv->sessions); - for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { - PurpleMediaSession *session = sessions->data; - if (session->session) { - g_object_unref(session->session); - session->session = NULL; - } - } - } - - if (priv->participants) { - GList *participants = g_hash_table_get_values(priv->participants); - for (; participants; participants = g_list_delete_link(participants, participants)) - g_object_unref(participants->data); - } - - if (priv->manager) { - GstElement *pipeline = purple_media_manager_get_pipeline( - priv->manager); - GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); - g_signal_handlers_disconnect_matched(G_OBJECT(bus), - G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, - 0, 0, 0, media_bus_call, media); - gst_object_unref(bus); - - 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); - - 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_setup_pipeline(PurpleMedia *media) -{ - GstBus *bus; - gchar *name; - GstElement *pipeline; - - if (media->priv->conference == NULL || media->priv->manager == NULL) - return; - - pipeline = purple_media_manager_get_pipeline(media->priv->manager); - - name = g_strdup_printf("conf_%p", - media->priv->conference); - media->priv->confbin = gst_bin_new(name); - g_free(name); - - bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); - g_signal_connect(G_OBJECT(bus), "message", - G_CALLBACK(media_bus_call), media); - gst_object_unref(bus); - - gst_bin_add(GST_BIN(pipeline), - media->priv->confbin); - gst_bin_add(GST_BIN(media->priv->confbin), - GST_ELEMENT(media->priv->conference)); - gst_element_set_state(GST_ELEMENT(media->priv->confbin), - GST_STATE_PLAYING); -} - -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_get_object(value); - g_object_ref(media->priv->manager); - - purple_media_setup_pipeline(media); - break; - case PROP_ACCOUNT: - media->priv->account = g_value_get_pointer(value); - break; - case PROP_CONFERENCE: { - if (media->priv->conference) - gst_object_unref(media->priv->conference); - media->priv->conference = g_value_get_object(value); - gst_object_ref(media->priv->conference); - - purple_media_setup_pipeline(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_ACCOUNT: - g_value_set_pointer(value, media->priv->account); - break; - case PROP_CONFERENCE: - g_value_set_object(value, media->priv->conference); - 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; - } - -} -#endif - -/* - * PurpleMediaCandidateType - */ - -GType -purple_media_candidate_type_get_type() -{ - static GType type = 0; - if (type == 0) { - static const GEnumValue values[] = { - { PURPLE_MEDIA_CANDIDATE_TYPE_HOST, - "PURPLE_MEDIA_CANDIDATE_TYPE_HOST", - "host" }, - { PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX, - "PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX", - "srflx" }, - { PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX, - "PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX", - "prflx" }, - { PURPLE_MEDIA_CANDIDATE_TYPE_RELAY, - "PPURPLE_MEDIA_CANDIDATE_TYPE_RELAY", - "relay" }, - { PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST, - "PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST", - "multicast" }, - { 0, NULL, NULL } - }; - type = g_enum_register_static("PurpleMediaCandidateType", - values); - } - return type; -} - -/* - * PurpleMediaNetworkProtocol - */ - -GType -purple_media_network_protocol_get_type() -{ - static GType type = 0; - if (type == 0) { - static const GEnumValue values[] = { - { PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, - "PURPLE_MEDIA_NETWORK_PROTOCOL_UDP", - "udp" }, - { PURPLE_MEDIA_NETWORK_PROTOCOL_TCP, - "PURPLE_MEDIA_NETWORK_PROTOCOL_TCP", - "tcp" }, - { 0, NULL, NULL } - }; - type = g_enum_register_static("PurpleMediaNetworkProtocol", - values); - } - return type; -} - -/* - * PurpleMediaCandidate - */ - -struct _PurpleMediaCandidateClass -{ - GObjectClass parent_class; -}; - -struct _PurpleMediaCandidate -{ - GObject parent; -}; - -#ifdef USE_VV -struct _PurpleMediaCandidatePrivate -{ - gchar *foundation; - guint component_id; - gchar *ip; - guint16 port; - gchar *base_ip; - guint16 base_port; - PurpleMediaNetworkProtocol proto; - guint32 priority; - PurpleMediaCandidateType type; - gchar *username; - gchar *password; - guint ttl; -}; - -enum { - PROP_CANDIDATE_0, - PROP_FOUNDATION, - PROP_COMPONENT_ID, - PROP_IP, - PROP_PORT, - PROP_BASE_IP, - PROP_BASE_PORT, - PROP_PROTOCOL, - PROP_PRIORITY, - PROP_TYPE, - PROP_USERNAME, - PROP_PASSWORD, - PROP_TTL, -}; - -static void -purple_media_candidate_init(PurpleMediaCandidate *info) -{ - PurpleMediaCandidatePrivate *priv = - PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info); - priv->foundation = NULL; - priv->component_id = 0; - priv->ip = NULL; - priv->port = 0; - priv->base_ip = NULL; - priv->proto = PURPLE_MEDIA_NETWORK_PROTOCOL_UDP; - priv->priority = 0; - priv->type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST; - priv->username = NULL; - priv->password = NULL; - priv->ttl = 0; -} - -static void -purple_media_candidate_finalize(GObject *info) -{ - PurpleMediaCandidatePrivate *priv = - PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info); - - g_free(priv->foundation); - g_free(priv->ip); - g_free(priv->base_ip); - g_free(priv->username); - g_free(priv->password); -} - -static void -purple_media_candidate_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - PurpleMediaCandidatePrivate *priv; - g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object)); - - priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object); - - switch (prop_id) { - case PROP_FOUNDATION: - g_free(priv->foundation); - priv->foundation = g_value_dup_string(value); - break; - case PROP_COMPONENT_ID: - priv->component_id = g_value_get_uint(value); - break; - case PROP_IP: - g_free(priv->ip); - priv->ip = g_value_dup_string(value); - break; - case PROP_PORT: - priv->port = g_value_get_uint(value); - break; - case PROP_BASE_IP: - g_free(priv->base_ip); - priv->base_ip = g_value_dup_string(value); - break; - case PROP_BASE_PORT: - priv->base_port = g_value_get_uint(value); - break; - case PROP_PROTOCOL: - priv->proto = g_value_get_enum(value); - break; - case PROP_PRIORITY: - priv->priority = g_value_get_uint(value); - break; - case PROP_TYPE: - priv->type = g_value_get_enum(value); - break; - case PROP_USERNAME: - g_free(priv->username); - priv->username = g_value_dup_string(value); - break; - case PROP_PASSWORD: - g_free(priv->password); - priv->password = g_value_dup_string(value); - break; - case PROP_TTL: - priv->ttl = g_value_get_uint(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID( - object, prop_id, pspec); - break; - } -} - -static void -purple_media_candidate_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - PurpleMediaCandidatePrivate *priv; - g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object)); - - priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object); - - switch (prop_id) { - case PROP_FOUNDATION: - g_value_set_string(value, priv->foundation); - break; - case PROP_COMPONENT_ID: - g_value_set_uint(value, priv->component_id); - break; - case PROP_IP: - g_value_set_string(value, priv->ip); - break; - case PROP_PORT: - g_value_set_uint(value, priv->port); - break; - case PROP_BASE_IP: - g_value_set_string(value, priv->base_ip); - break; - case PROP_BASE_PORT: - g_value_set_uint(value, priv->base_port); - break; - case PROP_PROTOCOL: - g_value_set_enum(value, priv->proto); - break; - case PROP_PRIORITY: - g_value_set_uint(value, priv->priority); - break; - case PROP_TYPE: - g_value_set_enum(value, priv->type); - break; - case PROP_USERNAME: - g_value_set_string(value, priv->username); - break; - case PROP_PASSWORD: - g_value_set_string(value, priv->password); - break; - case PROP_TTL: - g_value_set_uint(value, priv->ttl); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID( - object, prop_id, pspec); - break; - } -} - -static void -purple_media_candidate_class_init(PurpleMediaCandidateClass *klass) -{ - GObjectClass *gobject_class = (GObjectClass*)klass; - - gobject_class->finalize = purple_media_candidate_finalize; - gobject_class->set_property = purple_media_candidate_set_property; - gobject_class->get_property = purple_media_candidate_get_property; - - g_object_class_install_property(gobject_class, PROP_FOUNDATION, - g_param_spec_string("foundation", - "Foundation", - "The foundation of the candidate.", - NULL, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_COMPONENT_ID, - g_param_spec_uint("component-id", - "Component ID", - "The component id of the candidate.", - 0, G_MAXUINT, 0, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_IP, - g_param_spec_string("ip", - "IP Address", - "The IP address of the candidate.", - NULL, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_PORT, - g_param_spec_uint("port", - "Port", - "The port of the candidate.", - 0, G_MAXUINT16, 0, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_BASE_IP, - g_param_spec_string("base-ip", - "Base IP", - "The internal IP address of the candidate.", - NULL, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_BASE_PORT, - g_param_spec_uint("base-port", - "Base Port", - "The internal port of the candidate.", - 0, G_MAXUINT16, 0, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_PROTOCOL, - g_param_spec_enum("protocol", - "Protocol", - "The protocol of the candidate.", - PURPLE_TYPE_MEDIA_NETWORK_PROTOCOL, - PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_PRIORITY, - g_param_spec_uint("priority", - "Priority", - "The priority of the candidate.", - 0, G_MAXUINT32, 0, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_TYPE, - g_param_spec_enum("type", - "Type", - "The type of the candidate.", - PURPLE_TYPE_MEDIA_CANDIDATE_TYPE, - PURPLE_MEDIA_CANDIDATE_TYPE_HOST, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_USERNAME, - g_param_spec_string("username", - "Username", - "The username used to connect to the candidate.", - NULL, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_PASSWORD, - g_param_spec_string("password", - "Password", - "The password use to connect to the candidate.", - NULL, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_TTL, - g_param_spec_uint("ttl", - "TTL", - "The TTL of the candidate.", - 0, G_MAXUINT, 0, - G_PARAM_READWRITE)); - - g_type_class_add_private(klass, sizeof(PurpleMediaCandidatePrivate)); -} - -G_DEFINE_TYPE(PurpleMediaCandidate, - purple_media_candidate, G_TYPE_OBJECT); -#else -GType -purple_media_candidate_get_type() -{ - return G_TYPE_NONE; -} -#endif - -PurpleMediaCandidate * -purple_media_candidate_new(const gchar *foundation, guint component_id, - PurpleMediaCandidateType type, - PurpleMediaNetworkProtocol proto, - const gchar *ip, guint port) -{ - return g_object_new(PURPLE_TYPE_MEDIA_CANDIDATE, - "foundation", foundation, - "component-id", component_id, - "type", type, - "protocol", proto, - "ip", ip, - "port", port, NULL); -} - -static PurpleMediaCandidate * -purple_media_candidate_copy(PurpleMediaCandidate *candidate) -{ -#ifdef USE_VV - PurpleMediaCandidatePrivate *priv; - PurpleMediaCandidate *new_candidate; - - if (candidate == NULL) - return NULL; - - priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(candidate); - - new_candidate = purple_media_candidate_new(priv->foundation, - priv->component_id, priv->type, priv->proto, - priv->ip, priv->port); - g_object_set(new_candidate, - "base-ip", priv->base_ip, - "base-port", priv->base_port, - "priority", priv->priority, - "username", priv->username, - "password", priv->password, - "ttl", priv->ttl, NULL); - return new_candidate; -#else - return NULL; -#endif -} - -#ifdef USE_VV -static FsCandidate * -purple_media_candidate_to_fs(PurpleMediaCandidate *candidate) -{ - PurpleMediaCandidatePrivate *priv; - FsCandidate *fscandidate; - - if (candidate == NULL) - return NULL; - - priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(candidate); - - fscandidate = fs_candidate_new(priv->foundation, - priv->component_id, priv->type, - priv->proto, priv->ip, priv->port); - - fscandidate->base_ip = g_strdup(priv->base_ip); - fscandidate->base_port = priv->base_port; - fscandidate->priority = priv->priority; - fscandidate->username = g_strdup(priv->username); - fscandidate->password = g_strdup(priv->password); - fscandidate->ttl = priv->ttl; - return fscandidate; -} - -static PurpleMediaCandidate * -purple_media_candidate_from_fs(FsCandidate *fscandidate) -{ - PurpleMediaCandidate *candidate; - - if (fscandidate == NULL) - return NULL; - - candidate = purple_media_candidate_new(fscandidate->foundation, - fscandidate->component_id, fscandidate->type, - fscandidate->proto, fscandidate->ip, fscandidate->port); - g_object_set(candidate, - "base-ip", fscandidate->base_ip, - "base-port", fscandidate->base_port, - "priority", fscandidate->priority, - "username", fscandidate->username, - "password", fscandidate->password, - "ttl", fscandidate->ttl, NULL); - return candidate; -} - -static GList * -purple_media_candidate_list_from_fs(GList *candidates) -{ - GList *new_list = NULL; - - for (; candidates; candidates = g_list_next(candidates)) { - new_list = g_list_prepend(new_list, - purple_media_candidate_from_fs( - candidates->data)); - } - - new_list = g_list_reverse(new_list); - return new_list; -} - -static GList * -purple_media_candidate_list_to_fs(GList *candidates) -{ - GList *new_list = NULL; - - for (; candidates; candidates = g_list_next(candidates)) { - new_list = g_list_prepend(new_list, - purple_media_candidate_to_fs( - candidates->data)); - } - - new_list = g_list_reverse(new_list); - return new_list; -} -#endif - -GList * -purple_media_candidate_list_copy(GList *candidates) -{ - GList *new_list = NULL; - - for (; candidates; candidates = g_list_next(candidates)) { - new_list = g_list_prepend(new_list, - purple_media_candidate_copy(candidates->data)); - } - - new_list = g_list_reverse(new_list); - return new_list; -} - -void -purple_media_candidate_list_free(GList *candidates) -{ - for (; candidates; candidates = - g_list_delete_link(candidates, candidates)) { - g_object_unref(candidates->data); - } -} - -gchar * -purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate) -{ - gchar *foundation; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); - g_object_get(candidate, "foundation", &foundation, NULL); - return foundation; -} - -guint -purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate) -{ - guint component_id; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); - g_object_get(candidate, "component-id", &component_id, NULL); - return component_id; -} - -gchar * -purple_media_candidate_get_ip(PurpleMediaCandidate *candidate) -{ - gchar *ip; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); - g_object_get(candidate, "ip", &ip, NULL); - return ip; -} - -guint16 -purple_media_candidate_get_port(PurpleMediaCandidate *candidate) -{ - guint port; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); - g_object_get(candidate, "port", &port, NULL); - return port; -} - -gchar * -purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate) -{ - gchar *base_ip; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); - g_object_get(candidate, "base-ip", &base_ip, NULL); - return base_ip; -} - -guint16 -purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate) -{ - guint base_port; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); - g_object_get(candidate, "base_port", &base_port, NULL); - return base_port; -} - -PurpleMediaNetworkProtocol -purple_media_candidate_get_protocol(PurpleMediaCandidate *candidate) -{ - PurpleMediaNetworkProtocol protocol; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), - PURPLE_MEDIA_NETWORK_PROTOCOL_UDP); - g_object_get(candidate, "protocol", &protocol, NULL); - return protocol; -} - -guint32 -purple_media_candidate_get_priority(PurpleMediaCandidate *candidate) -{ - guint priority; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); - g_object_get(candidate, "priority", &priority, NULL); - return priority; -} - -PurpleMediaCandidateType -purple_media_candidate_get_candidate_type(PurpleMediaCandidate *candidate) -{ - PurpleMediaCandidateType type; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), - PURPLE_MEDIA_CANDIDATE_TYPE_HOST); - g_object_get(candidate, "type", &type, NULL); - return type; -} - -gchar * -purple_media_candidate_get_username(PurpleMediaCandidate *candidate) -{ - gchar *username; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); - g_object_get(candidate, "username", &username, NULL); - return username; -} - -gchar * -purple_media_candidate_get_password(PurpleMediaCandidate *candidate) -{ - gchar *password; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); - g_object_get(candidate, "password", &password, NULL); - return password; -} - -guint -purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate) -{ - guint ttl; - g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); - g_object_get(candidate, "ttl", &ttl, NULL); - return ttl; -} - -#ifdef USE_VV -static FsMediaType -purple_media_to_fs_media_type(PurpleMediaSessionType type) -{ - if (type & PURPLE_MEDIA_AUDIO) - return FS_MEDIA_TYPE_AUDIO; - else if (type & PURPLE_MEDIA_VIDEO) - return FS_MEDIA_TYPE_VIDEO; - else - return 0; -} - -static FsStreamDirection -purple_media_to_fs_stream_direction(PurpleMediaSessionType type) -{ - if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_AUDIO || - (type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO) - return FS_DIRECTION_BOTH; - else if ((type & PURPLE_MEDIA_SEND_AUDIO) || - (type & PURPLE_MEDIA_SEND_VIDEO)) - return FS_DIRECTION_SEND; - else if ((type & PURPLE_MEDIA_RECV_AUDIO) || - (type & PURPLE_MEDIA_RECV_VIDEO)) - return FS_DIRECTION_RECV; - else - return FS_DIRECTION_NONE; -} - -static PurpleMediaSessionType -purple_media_from_fs(FsMediaType type, FsStreamDirection direction) -{ - PurpleMediaSessionType result = PURPLE_MEDIA_NONE; - if (type == FS_MEDIA_TYPE_AUDIO) { - if (direction & FS_DIRECTION_SEND) - result |= PURPLE_MEDIA_SEND_AUDIO; - if (direction & FS_DIRECTION_RECV) - result |= PURPLE_MEDIA_RECV_AUDIO; - } else if (type == FS_MEDIA_TYPE_VIDEO) { - if (direction & FS_DIRECTION_SEND) - result |= PURPLE_MEDIA_SEND_VIDEO; - if (direction & FS_DIRECTION_RECV) - result |= PURPLE_MEDIA_RECV_VIDEO; - } - return result; -} -#endif - -/* - * PurpleMediaCodec - */ - -struct _PurpleMediaCodecClass -{ - GObjectClass parent_class; -}; - -struct _PurpleMediaCodec -{ - GObject parent; -}; - -#ifdef USE_VV -struct _PurpleMediaCodecPrivate -{ - gint id; - char *encoding_name; - PurpleMediaSessionType media_type; - guint clock_rate; - guint channels; - GList *optional_params; -}; - -enum { - PROP_CODEC_0, - PROP_ID, - PROP_ENCODING_NAME, - PROP_MEDIA_TYPE, - PROP_CLOCK_RATE, - PROP_CHANNELS, - PROP_OPTIONAL_PARAMS, -}; - -static void -purple_media_codec_init(PurpleMediaCodec *info) -{ - PurpleMediaCodecPrivate *priv = - PURPLE_MEDIA_CODEC_GET_PRIVATE(info); - priv->encoding_name = NULL; - priv->optional_params = NULL; -} - -static void -purple_media_codec_finalize(GObject *info) -{ - PurpleMediaCodecPrivate *priv = - PURPLE_MEDIA_CODEC_GET_PRIVATE(info); - g_free(priv->encoding_name); - for (; priv->optional_params; priv->optional_params = - g_list_delete_link(priv->optional_params, - priv->optional_params)) { - g_free(priv->optional_params->data); - } -} - -static void -purple_media_codec_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - PurpleMediaCodecPrivate *priv; - g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object)); - - priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object); - - switch (prop_id) { - case PROP_ID: - priv->id = g_value_get_uint(value); - break; - case PROP_ENCODING_NAME: - g_free(priv->encoding_name); - priv->encoding_name = g_value_dup_string(value); - break; - case PROP_MEDIA_TYPE: - priv->media_type = g_value_get_flags(value); - break; - case PROP_CLOCK_RATE: - priv->clock_rate = g_value_get_uint(value); - break; - case PROP_CHANNELS: - priv->channels = g_value_get_uint(value); - break; - case PROP_OPTIONAL_PARAMS: - priv->optional_params = g_value_get_pointer(value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID( - object, prop_id, pspec); - break; - } -} - -static void -purple_media_codec_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - PurpleMediaCodecPrivate *priv; - g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object)); - - priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object); - - switch (prop_id) { - case PROP_ID: - g_value_set_uint(value, priv->id); - break; - case PROP_ENCODING_NAME: - g_value_set_string(value, priv->encoding_name); - break; - case PROP_MEDIA_TYPE: - g_value_set_flags(value, priv->media_type); - break; - case PROP_CLOCK_RATE: - g_value_set_uint(value, priv->clock_rate); - break; - case PROP_CHANNELS: - g_value_set_uint(value, priv->channels); - break; - case PROP_OPTIONAL_PARAMS: - g_value_set_pointer(value, priv->optional_params); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID( - object, prop_id, pspec); - break; - } -} - -static void -purple_media_codec_class_init(PurpleMediaCodecClass *klass) -{ - GObjectClass *gobject_class = (GObjectClass*)klass; - - gobject_class->finalize = purple_media_codec_finalize; - gobject_class->set_property = purple_media_codec_set_property; - gobject_class->get_property = purple_media_codec_get_property; - - g_object_class_install_property(gobject_class, PROP_ID, - g_param_spec_uint("id", - "ID", - "The numeric identifier of the codec.", - 0, G_MAXUINT, 0, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_ENCODING_NAME, - g_param_spec_string("encoding-name", - "Encoding Name", - "The name of the codec.", - NULL, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE, - g_param_spec_flags("media-type", - "Media Type", - "Whether this is an audio of video codec.", - PURPLE_TYPE_MEDIA_SESSION_TYPE, - PURPLE_MEDIA_NONE, - G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_CLOCK_RATE, - g_param_spec_uint("clock-rate", - "Create Callback", - "The function called to create this element.", - 0, G_MAXUINT, 0, - G_PARAM_READWRITE)); - - g_object_class_install_property(gobject_class, PROP_CHANNELS, - g_param_spec_uint("channels", - "Channels", - "The number of channels in this codec.", - 0, G_MAXUINT, 0, - G_PARAM_READWRITE)); - g_object_class_install_property(gobject_class, PROP_OPTIONAL_PARAMS, - g_param_spec_pointer("optional-params", - "Optional Params", - "A list of optional parameters for the codec.", - G_PARAM_READWRITE)); - - g_type_class_add_private(klass, sizeof(PurpleMediaCodecPrivate)); -} - -G_DEFINE_TYPE(PurpleMediaCodec, - purple_media_codec, G_TYPE_OBJECT); -#else -GType -purple_media_codec_get_type() -{ - return G_TYPE_NONE; -} -#endif - -guint -purple_media_codec_get_id(PurpleMediaCodec *codec) -{ - guint id; - g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0); - g_object_get(codec, "id", &id, NULL); - return id; -} - -gchar * -purple_media_codec_get_encoding_name(PurpleMediaCodec *codec) -{ - gchar *name; - g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL); - g_object_get(codec, "encoding-name", &name, NULL); - return name; -} - -guint -purple_media_codec_get_clock_rate(PurpleMediaCodec *codec) -{ - guint clock_rate; - g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0); - g_object_get(codec, "clock-rate", &clock_rate, NULL); - return clock_rate; -} - -guint -purple_media_codec_get_channels(PurpleMediaCodec *codec) -{ - guint channels; - g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0); - g_object_get(codec, "channels", &channels, NULL); - return channels; -} - -GList * -purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec) -{ - GList *optional_params; - g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL); - g_object_get(codec, "optional-params", &optional_params, NULL); - return optional_params; -} - -void -purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec, - const gchar *name, const gchar *value) -{ -#ifdef USE_VV - PurpleMediaCodecPrivate *priv; - PurpleKeyValuePair *new_param; - - g_return_if_fail(codec != NULL); - g_return_if_fail(name != NULL && value != NULL); - - priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); - - new_param = g_new0(PurpleKeyValuePair, 1); - new_param->key = g_strdup(name); - new_param->value = g_strdup(value); - priv->optional_params = g_list_append( - priv->optional_params, new_param); -#endif -} - -void -purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec, - PurpleKeyValuePair *param) -{ -#ifdef USE_VV - PurpleMediaCodecPrivate *priv; - - g_return_if_fail(codec != NULL && param != NULL); - - priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); - - g_free(param->key); - g_free(param->value); - g_free(param); - - priv->optional_params = - g_list_remove(priv->optional_params, param); -#endif -} - -PurpleKeyValuePair * -purple_media_codec_get_optional_parameter(PurpleMediaCodec *codec, - const gchar *name, const gchar *value) -{ -#ifdef USE_VV - PurpleMediaCodecPrivate *priv; - GList *iter; - - g_return_val_if_fail(codec != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); - - priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); - - for (iter = priv->optional_params; iter; iter = g_list_next(iter)) { - PurpleKeyValuePair *param = iter->data; - if (!g_ascii_strcasecmp(param->key, name) && - (value == NULL || - !g_ascii_strcasecmp(param->value, value))) - return param; - } -#endif - - return NULL; -} - -PurpleMediaCodec * -purple_media_codec_new(int id, const char *encoding_name, - PurpleMediaSessionType media_type, guint clock_rate) -{ - PurpleMediaCodec *codec = - g_object_new(PURPLE_TYPE_MEDIA_CODEC, - "id", id, - "encoding_name", encoding_name, - "media_type", media_type, - "clock-rate", clock_rate, NULL); - return codec; -} - -static PurpleMediaCodec * -purple_media_codec_copy(PurpleMediaCodec *codec) -{ -#ifdef USE_VV - PurpleMediaCodecPrivate *priv; - PurpleMediaCodec *new_codec; - GList *iter; - - if (codec == NULL) - return NULL; - - priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); - - new_codec = purple_media_codec_new(priv->id, priv->encoding_name, - priv->media_type, priv->clock_rate); - g_object_set(codec, "channels", priv->channels, NULL); - - for (iter = priv->optional_params; iter; iter = g_list_next(iter)) { - PurpleKeyValuePair *param = - (PurpleKeyValuePair*)iter->data; - purple_media_codec_add_optional_parameter(new_codec, - param->key, param->value); - } - - return new_codec; -#else - return NULL; -#endif -} - -#ifdef USE_VV -static FsCodec * -purple_media_codec_to_fs(const PurpleMediaCodec *codec) -{ - PurpleMediaCodecPrivate *priv; - FsCodec *new_codec; - GList *iter; - - if (codec == NULL) - return NULL; - - priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); - - new_codec = fs_codec_new(priv->id, priv->encoding_name, - purple_media_to_fs_media_type(priv->media_type), - priv->clock_rate); - new_codec->channels = priv->channels; - - for (iter = priv->optional_params; iter; iter = g_list_next(iter)) { - PurpleKeyValuePair *param = (PurpleKeyValuePair*)iter->data; - fs_codec_add_optional_parameter(new_codec, - param->key, param->value); - } - - return new_codec; -} - -static PurpleMediaCodec * -purple_media_codec_from_fs(const FsCodec *codec) -{ - PurpleMediaCodec *new_codec; - GList *iter; - - if (codec == NULL) - return NULL; - - new_codec = purple_media_codec_new(codec->id, codec->encoding_name, - purple_media_from_fs(codec->media_type, - FS_DIRECTION_BOTH), codec->clock_rate); - g_object_set(new_codec, "channels", codec->channels, NULL); - - for (iter = codec->optional_params; iter; iter = g_list_next(iter)) { - FsCodecParameter *param = (FsCodecParameter*)iter->data; - purple_media_codec_add_optional_parameter(new_codec, - param->name, param->value); - } - - return new_codec; -} -#endif - -gchar * -purple_media_codec_to_string(const PurpleMediaCodec *codec) -{ -#ifdef USE_VV - FsCodec *fscodec = purple_media_codec_to_fs(codec); - gchar *str = fs_codec_to_string(fscodec); - fs_codec_destroy(fscodec); - return str; -#else - return g_strdup(""); -#endif -} - -#ifdef USE_VV -static GList * -purple_media_codec_list_from_fs(GList *codecs) -{ - GList *new_list = NULL; - - for (; codecs; codecs = g_list_next(codecs)) { - new_list = g_list_prepend(new_list, - purple_media_codec_from_fs( - codecs->data)); - } - - new_list = g_list_reverse(new_list); - return new_list; -} - -static GList * -purple_media_codec_list_to_fs(GList *codecs) -{ - GList *new_list = NULL; - - for (; codecs; codecs = g_list_next(codecs)) { - new_list = g_list_prepend(new_list, - purple_media_codec_to_fs( - codecs->data)); - } - - new_list = g_list_reverse(new_list); - return new_list; -} -#endif - -GList * -purple_media_codec_list_copy(GList *codecs) -{ - GList *new_list = NULL; - - for (; codecs; codecs = g_list_next(codecs)) { - new_list = g_list_prepend(new_list, - purple_media_codec_copy(codecs->data)); - } - - new_list = g_list_reverse(new_list); - return new_list; -} - -void -purple_media_codec_list_free(GList *codecs) -{ - for (; codecs; codecs = - g_list_delete_link(codecs, codecs)) { - g_object_unref(codecs->data); - } -} - -#ifdef USE_VV -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 FsParticipant* -purple_media_get_participant(PurpleMedia *media, const gchar *name) -{ - g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); - return (FsParticipant*) (media->priv->participants) ? - g_hash_table_lookup(media->priv->participants, name) : 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); -} - -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); -} - -static FsParticipant * -purple_media_add_participant(PurpleMedia *media, const gchar *name) -{ - FsParticipant *participant; - GError *err = NULL; - - g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); - - participant = purple_media_get_participant(media, name); - - if (participant) - return participant; - - participant = fs_conference_new_participant(media->priv->conference, - (gchar*)name, &err); - - if (err) { - purple_debug_error("media", "Error creating participant: %s\n", - err->message); - g_error_free(err); - return NULL; - } - - if (!media->priv->participants) { - purple_debug_info("media", "Creating hash table for participants\n"); - media->priv->participants = g_hash_table_new_full(g_str_hash, - g_str_equal, g_free, NULL); - } - - g_hash_table_insert(media->priv->participants, g_strdup(name), participant); - - return participant; -} - -static PurpleMediaStream * -purple_media_insert_stream(PurpleMediaSession *session, const gchar *name, FsStream *stream) -{ - PurpleMediaStream *media_stream; - - g_return_val_if_fail(session != NULL, NULL); - - media_stream = g_new0(PurpleMediaStream, 1); - media_stream->stream = stream; - media_stream->participant = g_strdup(name); - media_stream->session = session; - - 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, - FsCandidate *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_VV -static void -purple_media_set_src(PurpleMedia *media, const gchar *sess_id, GstElement *src) -{ - PurpleMediaSession *session; - GstPad *sinkpad; - GstPad *srcpad; - - g_return_if_fail(PURPLE_IS_MEDIA(media)); - g_return_if_fail(GST_IS_ELEMENT(src)); - - session = purple_media_get_session(media, sess_id); - - if (session == NULL) { - purple_debug_warning("media", "purple_media_set_src: trying" - " to set src on non-existent session\n"); - return; - } - - if (session->src) - gst_object_unref(session->src); - session->src = src; - gst_element_set_locked_state(session->src, TRUE); - - session->tee = gst_element_factory_make("tee", NULL); - gst_bin_add(GST_BIN(session->media->priv->confbin), session->tee); - - /* This supposedly isn't necessary, but it silences some warnings */ - if (GST_ELEMENT_PARENT(session->media->priv->confbin) - == GST_ELEMENT_PARENT(session->src)) { - GstPad *pad = gst_element_get_static_pad(session->tee, "sink"); - GstPad *ghost = gst_ghost_pad_new(NULL, pad); - gst_object_unref(pad); - gst_pad_set_active(ghost, TRUE); - gst_element_add_pad(session->media->priv->confbin, ghost); - } - - gst_element_set_state(session->tee, GST_STATE_PLAYING); - gst_element_link(session->src, session->media->priv->confbin); - - g_object_get(session->session, "sink-pad", &sinkpad, NULL); - if (session->type & PURPLE_MEDIA_SEND_AUDIO) { - gchar *name = g_strdup_printf("volume_%s", session->id); - GstElement *level; - GstElement *volume = gst_element_factory_make("volume", name); - double input_volume = purple_prefs_get_int( - "/purple/media/audio/volume/input")/10.0; - g_free(name); - name = g_strdup_printf("sendlevel_%s", session->id); - level = gst_element_factory_make("level", name); - g_free(name); - gst_bin_add(GST_BIN(session->media->priv->confbin), volume); - gst_bin_add(GST_BIN(session->media->priv->confbin), level); - gst_element_set_state(level, GST_STATE_PLAYING); - gst_element_set_state(volume, GST_STATE_PLAYING); - gst_element_link(volume, level); - gst_element_link(session->tee, volume); - srcpad = gst_element_get_static_pad(level, "src"); - g_object_set(volume, "volume", input_volume, NULL); - } else { - srcpad = gst_element_get_request_pad(session->tee, "src%d"); - } - purple_debug_info("media", "connecting pad: %s\n", - gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK - ? "success" : "failure"); - gst_element_set_locked_state(session->src, FALSE); - gst_object_unref(session->src); -} -#endif - -#ifdef USE_GSTREAMER -GstElement * -purple_media_get_src(PurpleMedia *media, const gchar *sess_id) -{ -#ifdef USE_VV - PurpleMediaSession *session; - g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); - session = purple_media_get_session(media, sess_id); - return (session != NULL) ? session->src : NULL; -#else - return NULL; -#endif -} -#endif /* USE_GSTREAMER */ - -#ifdef USE_VV -static PurpleMediaSession * -purple_media_session_from_fs_stream(PurpleMedia *media, FsStream *stream) -{ - FsSession *fssession; - GList *values; - - g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); - g_return_val_if_fail(FS_IS_STREAM(stream), NULL); - - g_object_get(stream, "session", &fssession, NULL); - - values = g_hash_table_get_values(media->priv->sessions); - - for (; values; values = g_list_delete_link(values, values)) { - PurpleMediaSession *session = values->data; - - if (session->session == fssession) { - g_list_free(values); - g_object_unref(fssession); - return session; - } - } - - g_object_unref(fssession); - return NULL; -} - -static gboolean -media_bus_call(GstBus *bus, GstMessage *msg, PurpleMedia *media) -{ - switch(GST_MESSAGE_TYPE(msg)) { - case GST_MESSAGE_ELEMENT: { - if (g_signal_has_handler_pending(media, - purple_media_signals[LEVEL], 0, FALSE) - && gst_structure_has_name( - gst_message_get_structure(msg), "level")) { - GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); - gchar *name; - gchar *participant = NULL; - PurpleMediaSession *session = NULL; - gdouble rms_db; - gdouble percent; - const GValue *list; - const GValue *value; - - if (!PURPLE_IS_MEDIA(media) || - GST_ELEMENT_PARENT(src) != - media->priv->confbin) - break; - - name = gst_element_get_name(src); - if (!strncmp(name, "sendlevel_", 10)) { - session = purple_media_get_session( - media, name+10); - } else { - GList *iter = media->priv->streams; - for (; iter; iter = g_list_next(iter)) { - PurpleMediaStream *stream = iter->data; - if (stream->level == src) { - session = stream->session; - participant = stream->participant; - break; - } - } - } - g_free(name); - if (!session) - break; - - list = gst_structure_get_value( - gst_message_get_structure(msg), "rms"); - value = gst_value_list_get_value(list, 0); - rms_db = g_value_get_double(value); - percent = pow(10, rms_db / 20) * 5; - if(percent > 1.0) - percent = 1.0; - - g_signal_emit(media, purple_media_signals[LEVEL], - 0, session->id, participant, percent); - break; - } - if (!FS_IS_CONFERENCE(GST_MESSAGE_SRC(msg)) || - !PURPLE_IS_MEDIA(media) || - media->priv->conference != - FS_CONFERENCE(GST_MESSAGE_SRC(msg))) - break; - - if (gst_structure_has_name(msg->structure, "farsight-error")) { - FsError error_no; - gst_structure_get_enum(msg->structure, "error-no", - FS_TYPE_ERROR, (gint*)&error_no); - switch (error_no) { - case FS_ERROR_NO_CODECS: - purple_media_error(media, _("No codecs found. Install some GStreamer codecs found in GStreamer plugins packages.")); - purple_media_end(media, NULL, NULL); - break; - case FS_ERROR_NO_CODECS_LEFT: - purple_media_error(media, _("No codecs left. Your codec preferences in fs-codecs.conf are too strict.")); - purple_media_end(media, NULL, NULL); - break; - case FS_ERROR_UNKNOWN_CNAME: - /* - * Unknown CName is only a problem for the - * multicast transmitter which isn't used. - * It is also deprecated. - */ - break; - default: - purple_debug_error("media", "farsight-error: %i: %s\n", error_no, - gst_structure_get_string(msg->structure, "error-msg")); - break; - } - - if (FS_ERROR_IS_FATAL(error_no)) { - purple_media_error(media, _("A non-recoverable Farsight2 error has occurred.")); - purple_media_end(media, NULL, NULL); - } - } else if (gst_structure_has_name(msg->structure, - "farsight-new-local-candidate")) { - FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream")); - FsCandidate *local_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "candidate")); - PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream); - purple_media_new_local_candidate_cb(stream, local_candidate, session); - } else if (gst_structure_has_name(msg->structure, - "farsight-local-candidates-prepared")) { - FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream")); - PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream); - purple_media_candidates_prepared_cb(stream, session); - } else if (gst_structure_has_name(msg->structure, - "farsight-new-active-candidate-pair")) { - FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream")); - FsCandidate *local_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "local-candidate")); - FsCandidate *remote_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "remote-candidate")); - PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream); - purple_media_candidate_pair_established_cb(stream, local_candidate, remote_candidate, session); - } else if (gst_structure_has_name(msg->structure, - "farsight-recv-codecs-changed")) { - GList *codecs = g_value_get_boxed(gst_structure_get_value(msg->structure, "codecs")); - FsCodec *codec = codecs->data; - purple_debug_info("media", "farsight-recv-codecs-changed: %s\n", codec->encoding_name); - - } else if (gst_structure_has_name(msg->structure, - "farsight-component-state-changed")) { - FsStreamState fsstate = g_value_get_enum(gst_structure_get_value(msg->structure, "state")); - guint component = g_value_get_uint(gst_structure_get_value(msg->structure, "component")); - const gchar *state; - switch (fsstate) { - case FS_STREAM_STATE_FAILED: - state = "FAILED"; - break; - case FS_STREAM_STATE_DISCONNECTED: - state = "DISCONNECTED"; - break; - case FS_STREAM_STATE_GATHERING: - state = "GATHERING"; - break; - case FS_STREAM_STATE_CONNECTING: - state = "CONNECTING"; - break; - case FS_STREAM_STATE_CONNECTED: - state = "CONNECTED"; - break; - case FS_STREAM_STATE_READY: - state = "READY"; - break; - default: - state = "UNKNOWN"; - break; - } - purple_debug_info("media", "farsight-component-state-changed: component: %u state: %s\n", component, state); - } else if (gst_structure_has_name(msg->structure, - "farsight-send-codec-changed")) { - FsCodec *codec = g_value_get_boxed(gst_structure_get_value(msg->structure, "codec")); - gchar *codec_str = fs_codec_to_string(codec); - purple_debug_info("media", "farsight-send-codec-changed: codec: %s\n", codec_str); - g_free(codec_str); - } else if (gst_structure_has_name(msg->structure, - "farsight-codecs-changed")) { - GList *sessions = g_hash_table_get_values(PURPLE_MEDIA(media)->priv->sessions); - FsSession *fssession = g_value_get_object(gst_structure_get_value(msg->structure, "session")); - for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { - PurpleMediaSession *session = sessions->data; - if (session->session == fssession) { - gchar *session_id = g_strdup(session->id); - g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, session_id); - g_free(session_id); - g_list_free(sessions); - break; - } - } - } - break; - } - case GST_MESSAGE_ERROR: { - GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(msg)); - GstElement *lastElement = NULL; - while (!GST_IS_PIPELINE(element)) { - if (element == media->priv->confbin) { - purple_media_error(media, _("Conference error")); - purple_media_end(media, NULL, NULL); - break; - } - lastElement = element; - element = GST_ELEMENT_PARENT(element); - } - if (GST_IS_PIPELINE(element)) { - GList *sessions = g_hash_table_get_values(media->priv->sessions); - for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { - PurpleMediaSession *session = sessions->data; - - if (session->src == lastElement) { - if (session->type & PURPLE_MEDIA_AUDIO) - purple_media_error(media, _("Error with your microphone")); - else - purple_media_error(media, _("Error with your webcam")); - purple_media_end(media, NULL, NULL); - break; - } - } - g_list_free(sessions); - } - } - default: - break; - } - - return TRUE; -} -#endif - -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 - g_return_if_fail(PURPLE_IS_MEDIA(media)); - if (session_id == NULL && participant == NULL) { - g_signal_emit(media, purple_media_signals[STATE_CHANGED], - 0, PURPLE_MEDIA_STATE_END, - NULL, NULL); - g_object_unref(media); - } -#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; - - g_return_if_fail(PURPLE_IS_MEDIA(media)); - - streams = purple_media_get_streams(media, - session_id, participant); - - for (; streams; streams = - g_list_delete_link(streams, streams)) { - PurpleMediaStream *stream = streams->data; - g_object_set(G_OBJECT(stream->stream), "direction", - purple_media_to_fs_stream_direction( - stream->session->type), NULL); - stream->accepted = TRUE; - - if (stream->remote_candidates != NULL && - stream->initiator == FALSE) { - GError *err = NULL; - fs_stream_set_remote_candidates(stream->stream, - stream->remote_candidates, &err); - - if (err) { - purple_debug_error("media", "Error adding remote" - " candidates: %s\n", err->message); - g_error_free(err); - } - } - } - } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_MUTE || - type == PURPLE_MEDIA_INFO_UNMUTE)) { - GList *sessions; - gboolean active = (type == PURPLE_MEDIA_INFO_MUTE); - - g_return_if_fail(PURPLE_IS_MEDIA(media)); - - if (session_id == NULL) - sessions = g_hash_table_get_values( - media->priv->sessions); - else - sessions = g_list_prepend(NULL, - purple_media_get_session( - media, session_id)); - - purple_debug_info("media", "Turning mute %s\n", - active ? "on" : "off"); - - for (; sessions; sessions = g_list_delete_link( - sessions, sessions)) { - PurpleMediaSession *session = sessions->data; - if (session->type & PURPLE_MEDIA_SEND_AUDIO) { - gchar *name = g_strdup_printf("volume_%s", - session->id); - GstElement *volume = gst_bin_get_by_name( - GST_BIN(session->media-> - priv->confbin), name); - g_free(name); - g_object_set(volume, "mute", active, NULL); - } - } - } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_PAUSE || - type == PURPLE_MEDIA_INFO_UNPAUSE)) { - gboolean active = (type == PURPLE_MEDIA_INFO_PAUSE); - GList *streams = purple_media_get_streams(media, - session_id, participant); - for (; streams; streams = g_list_delete_link(streams, streams)) { - PurpleMediaStream *stream = streams->data; - if (stream->session->type & PURPLE_MEDIA_SEND_VIDEO) { - stream->paused = active; - - if (!stream->held) - g_object_set(stream->stream, "direction", - purple_media_to_fs_stream_direction( - stream->session->type & ((active) ? - ~PURPLE_MEDIA_SEND_VIDEO : - PURPLE_MEDIA_VIDEO)), NULL); - } - } - } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_HOLD || - type == PURPLE_MEDIA_INFO_UNHOLD)) { - GList *streams; - gboolean active = (type == PURPLE_MEDIA_INFO_HOLD); - - g_return_if_fail(PURPLE_IS_MEDIA(media)); - - streams = purple_media_get_streams(media, - session_id, participant); - for (; streams; streams = g_list_delete_link(streams, streams)) { - PurpleMediaStream *stream = streams->data; - stream->held = active; - if (stream->session->type & PURPLE_MEDIA_VIDEO) { - FsStreamDirection direction; - - direction = ((active) ? - ~PURPLE_MEDIA_VIDEO : - PURPLE_MEDIA_VIDEO); - if (!active && stream->paused) - direction &= ~PURPLE_MEDIA_SEND_VIDEO; - - g_object_set(stream->stream, "direction", - purple_media_to_fs_stream_direction( - stream->session->type & direction), NULL); - } else if (stream->session->type & PURPLE_MEDIA_AUDIO) { - g_object_set(stream->stream, "direction", - purple_media_to_fs_stream_direction( - stream->session->type & ((active) ? - ~PURPLE_MEDIA_AUDIO : - PURPLE_MEDIA_AUDIO)), NULL); - } - } - } - - g_signal_emit(media, purple_media_signals[STREAM_INFO], - 0, type, session_id, participant, local); - - if (type == PURPLE_MEDIA_INFO_HANGUP || - type == PURPLE_MEDIA_INFO_REJECT) { - purple_media_end(media, session_id, participant); - } -#endif -} - -#ifdef USE_VV -static void -purple_media_new_local_candidate_cb(FsStream *stream, - FsCandidate *local_candidate, - PurpleMediaSession *session) -{ - gchar *name; - FsParticipant *participant; - PurpleMediaCandidate *candidate; - - g_return_if_fail(FS_IS_STREAM(stream)); - g_return_if_fail(session != NULL); - - purple_debug_info("media", "got new local candidate: %s\n", local_candidate->foundation); - g_object_get(stream, "participant", &participant, NULL); - g_object_get(participant, "cname", &name, NULL); - g_object_unref(participant); - - purple_media_insert_local_candidate(session, name, fs_candidate_copy(local_candidate)); - - candidate = purple_media_candidate_from_fs(local_candidate); - g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE], - 0, session->id, name, candidate); - g_object_unref(candidate); - - g_free(name); -} - -static void -purple_media_candidates_prepared_cb(FsStream *stream, PurpleMediaSession *session) -{ - gchar *name; - FsParticipant *participant; - PurpleMediaStream *stream_data; - - g_return_if_fail(FS_IS_STREAM(stream)); - g_return_if_fail(session != NULL); - - g_object_get(stream, "participant", &participant, NULL); - g_object_get(participant, "cname", &name, NULL); - g_object_unref(participant); - - stream_data = purple_media_get_stream(session->media, session->id, name); - stream_data->candidates_prepared = TRUE; - - g_signal_emit(session->media, - purple_media_signals[CANDIDATES_PREPARED], - 0, session->id, name); - - g_free(name); -} - -/* callback called when a pair of transport candidates (local and remote) - * has been established */ -static void -purple_media_candidate_pair_established_cb(FsStream *fsstream, - FsCandidate *native_candidate, - FsCandidate *remote_candidate, - PurpleMediaSession *session) -{ - gchar *name; - FsParticipant *participant; - PurpleMediaStream *stream; - GList *iter; - - g_return_if_fail(FS_IS_STREAM(fsstream)); - g_return_if_fail(session != NULL); - - g_object_get(fsstream, "participant", &participant, NULL); - g_object_get(participant, "cname", &name, NULL); - g_object_unref(participant); - - stream = purple_media_get_stream(session->media, session->id, name); - - iter = stream->active_local_candidates; - for(; iter; iter = g_list_next(iter)) { - FsCandidate *c = iter->data; - if (native_candidate->component_id == c->component_id) { - fs_candidate_destroy(c); - stream->active_local_candidates = - g_list_delete_link(iter, iter); - stream->active_local_candidates = g_list_prepend( - stream->active_local_candidates, - fs_candidate_copy(native_candidate)); - break; - } - } - if (iter == NULL) - stream->active_local_candidates = g_list_prepend( - stream->active_local_candidates, - fs_candidate_copy(native_candidate)); - - iter = stream->active_remote_candidates; - for(; iter; iter = g_list_next(iter)) { - FsCandidate *c = iter->data; - if (native_candidate->component_id == c->component_id) { - fs_candidate_destroy(c); - stream->active_remote_candidates = - g_list_delete_link(iter, iter); - stream->active_remote_candidates = g_list_prepend( - stream->active_remote_candidates, - fs_candidate_copy(remote_candidate)); - break; - } - } - if (iter == NULL) - stream->active_remote_candidates = g_list_prepend( - stream->active_remote_candidates, - fs_candidate_copy(remote_candidate)); - - purple_debug_info("media", "candidate pair established\n"); -} - -static gboolean -purple_media_connected_cb(PurpleMediaStream *stream) -{ - g_return_val_if_fail(stream != NULL, FALSE); - - stream->connected_cb_id = 0; - - purple_media_manager_create_output_window( - stream->session->media->priv->manager, - stream->session->media, - stream->session->id, stream->participant); - - g_signal_emit(stream->session->media, - purple_media_signals[STATE_CHANGED], - 0, PURPLE_MEDIA_STATE_CONNECTED, - stream->session->id, stream->participant); - return FALSE; -} - -static void -purple_media_src_pad_added_cb(FsStream *fsstream, GstPad *srcpad, - FsCodec *codec, PurpleMediaStream *stream) -{ - PurpleMediaPrivate *priv; - GstPad *sinkpad; - - g_return_if_fail(FS_IS_STREAM(fsstream)); - g_return_if_fail(stream != NULL); - - priv = stream->session->media->priv; - - if (stream->src == NULL) { - GstElement *sink = NULL; - - if (codec->media_type == FS_MEDIA_TYPE_AUDIO) { - GstElement *queue = NULL; - double output_volume = purple_prefs_get_int( - "/purple/media/audio/volume/output")/10.0; - /* - * Should this instead be: - * audioconvert ! audioresample ! liveadder ! - * audioresample ! audioconvert ! realsink - */ - queue = gst_element_factory_make("queue", NULL); - stream->volume = gst_element_factory_make( - "volume", NULL); - g_object_set(stream->volume, "volume", - output_volume, NULL); - stream->level = gst_element_factory_make( - "level", NULL); - stream->src = gst_element_factory_make( - "liveadder", NULL); - sink = purple_media_manager_get_element(priv->manager, - PURPLE_MEDIA_RECV_AUDIO, - stream->session->media, - stream->session->id, - stream->participant); - gst_bin_add(GST_BIN(priv->confbin), queue); - gst_bin_add(GST_BIN(priv->confbin), stream->volume); - gst_bin_add(GST_BIN(priv->confbin), stream->level); - gst_bin_add(GST_BIN(priv->confbin), sink); - gst_element_set_state(sink, GST_STATE_PLAYING); - gst_element_set_state(stream->level, GST_STATE_PLAYING); - gst_element_set_state(stream->volume, GST_STATE_PLAYING); - gst_element_set_state(queue, GST_STATE_PLAYING); - gst_element_link(stream->level, sink); - gst_element_link(stream->volume, stream->level); - gst_element_link(queue, stream->volume); - sink = queue; - } else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) { - stream->src = gst_element_factory_make( - "fsfunnel", NULL); - sink = gst_element_factory_make( - "fakesink", NULL); - g_object_set(G_OBJECT(sink), "async", FALSE, NULL); - gst_bin_add(GST_BIN(priv->confbin), sink); - gst_element_set_state(sink, GST_STATE_PLAYING); - } - stream->tee = gst_element_factory_make("tee", NULL); - gst_bin_add_many(GST_BIN(priv->confbin), - stream->src, stream->tee, NULL); - gst_element_set_state(stream->tee, GST_STATE_PLAYING); - gst_element_set_state(stream->src, GST_STATE_PLAYING); - gst_element_link_many(stream->src, stream->tee, sink, NULL); - } - - sinkpad = gst_element_get_request_pad(stream->src, "sink%d"); - gst_pad_link(srcpad, sinkpad); - gst_object_unref(sinkpad); - - stream->connected_cb_id = purple_timeout_add(0, - (GSourceFunc)purple_media_connected_cb, stream); -} - -static void -purple_media_element_added_cb(FsElementAddedNotifier *self, - GstBin *bin, GstElement *element, gpointer user_data) -{ - /* - * Hack to make H264 work with Gmail video. - */ - if (!strncmp(GST_ELEMENT_NAME(element), "x264", 4)) { - g_object_set(GST_OBJECT(element), "cabac", FALSE, NULL); - } -} -#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; - FsParticipant *participant = NULL; - PurpleMediaStream *stream = NULL; - FsMediaType media_type = purple_media_to_fs_media_type(type); - FsStreamDirection type_direction = - purple_media_to_fs_stream_direction(type); - gboolean is_nice = !strcmp(transmitter, "nice"); - - g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); - - session = purple_media_get_session(media, sess_id); - - if (!session) { - GError *err = NULL; - GList *codec_conf = NULL, *iter = NULL; - gchar *filename = NULL; - PurpleMediaSessionType session_type; - GstElement *src = NULL; - - session = g_new0(PurpleMediaSession, 1); - - session->session = fs_conference_new_session( - media->priv->conference, media_type, &err); - - if (err != NULL) { - purple_media_error(media, _("Error creating session: %s"), err->message); - g_error_free(err); - g_free(session); - return FALSE; - } - - filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL); - codec_conf = fs_codec_list_from_keyfile(filename, &err); - g_free(filename); - - if (err != NULL) { - if (err->code == 4) - purple_debug_info("media", "Couldn't read " - "fs-codec.conf: %s\n", - err->message); - else - purple_debug_error("media", "Error reading " - "fs-codec.conf: %s\n", - err->message); - g_error_free(err); - } - - /* - * Add SPEEX if the configuration file doesn't exist or - * there isn't a speex entry. - */ - for (iter = codec_conf; iter; iter = g_list_next(iter)) { - FsCodec *codec = iter->data; - if (!g_ascii_strcasecmp(codec->encoding_name, "speex")) - break; - } - - if (iter == NULL) { - codec_conf = g_list_prepend(codec_conf, - fs_codec_new(FS_CODEC_ID_ANY, - "SPEEX", FS_MEDIA_TYPE_AUDIO, 8000)); - codec_conf = g_list_prepend(codec_conf, - fs_codec_new(FS_CODEC_ID_ANY, - "SPEEX", FS_MEDIA_TYPE_AUDIO, 16000)); - } - - fs_session_set_codec_preferences(session->session, codec_conf, NULL); - - /* - * Removes a 5-7 second delay before - * receiving the src-pad-added signal. - * Only works for non-multicast FsRtpSessions. - */ - if (is_nice || !strcmp(transmitter, "rawudp")) - g_object_set(G_OBJECT(session->session), - "no-rtcp-timeout", 0, NULL); - - /* - * Hack to make x264 work with Gmail video. - */ - if (is_nice && !strcmp(sess_id, "google-video")) { - FsElementAddedNotifier *notifier = - fs_element_added_notifier_new(); - g_signal_connect(G_OBJECT(notifier), "element-added", - G_CALLBACK(purple_media_element_added_cb), - stream); - fs_element_added_notifier_add(notifier, - GST_BIN(media->priv->conference)); - } - - fs_codec_list_destroy(codec_conf); - - 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 (type_direction & FS_DIRECTION_SEND) { - session_type = purple_media_from_fs(media_type, - FS_DIRECTION_SEND); - src = purple_media_manager_get_element( - media->priv->manager, session_type, - media, session->id, who); - if (!GST_IS_ELEMENT(src)) { - purple_debug_error("media", - "Error creating src for session %s\n", - session->id); - purple_media_end(media, session->id, NULL); - return FALSE; - } - - purple_media_set_src(media, session->id, src); - gst_element_set_state(session->src, GST_STATE_PLAYING); - purple_media_manager_create_output_window( - media->priv->manager, - session->media, - session->id, NULL); - } - } - - if (!(participant = purple_media_add_participant(media, who))) { - purple_media_remove_session(media, session); - g_free(session); - return FALSE; - } else { - g_signal_emit(media, purple_media_signals[STATE_CHANGED], - 0, PURPLE_MEDIA_STATE_NEW, - NULL, who); - } - - stream = purple_media_get_stream(media, sess_id, who); - - if (!stream) { - GError *err = NULL; - FsStream *fsstream = NULL; - const gchar *stun_ip = purple_network_get_stun_ip(); - const gchar *turn_ip = purple_network_get_turn_ip(); - - if (stun_ip || turn_ip) { - guint new_num_params = - (stun_ip && is_nice) && turn_ip ? - num_params + 2 : num_params + 1; - guint next_param_index = num_params; - GParameter *param = g_new0(GParameter, new_num_params); - memcpy(param, params, sizeof(GParameter) * num_params); - - if (stun_ip) { - purple_debug_info("media", - "setting property stun-ip on new stream: %s\n", stun_ip); - - param[next_param_index].name = "stun-ip"; - g_value_init(¶m[next_param_index].value, G_TYPE_STRING); - g_value_set_string(¶m[next_param_index].value, stun_ip); - next_param_index++; - } - - if (turn_ip && is_nice) { - GValueArray *relay_info = g_value_array_new(0); - GValue value; - gint turn_port = - purple_prefs_get_int("/purple/network/turn_port"); - const gchar *username = - purple_prefs_get_string("/purple/network/turn_username"); - const gchar *password = - purple_prefs_get_string("/purple/network/turn_password"); - GstStructure *turn_setup = gst_structure_new("relay-info", - "ip", G_TYPE_STRING, turn_ip, - "port", G_TYPE_UINT, turn_port, - "username", G_TYPE_STRING, username, - "password", G_TYPE_STRING, password, - NULL); - - if (turn_setup) { - memset(&value, 0, sizeof(GValue)); - g_value_init(&value, GST_TYPE_STRUCTURE); - gst_value_set_structure(&value, turn_setup); - relay_info = g_value_array_append(relay_info, &value); - gst_structure_free(turn_setup); - - purple_debug_info("media", - "setting property relay-info on new stream\n"); - param[next_param_index].name = "relay-info"; - g_value_init(¶m[next_param_index].value, - G_TYPE_VALUE_ARRAY); - g_value_set_boxed(¶m[next_param_index].value, - relay_info); - g_value_array_free(relay_info); - } else { - purple_debug_error("media", "Error relay info"); - g_object_unref(participant); - g_hash_table_remove(media->priv->participants, who); - purple_media_remove_session(media, session); - g_free(session); - return FALSE; - } - } - - fsstream = fs_session_new_stream(session->session, - participant, initiator == TRUE ? - type_direction : (type_direction & - FS_DIRECTION_RECV), transmitter, - new_num_params, param, &err); - g_free(param); - } else { - fsstream = fs_session_new_stream(session->session, - participant, initiator == TRUE ? - type_direction : (type_direction & - FS_DIRECTION_RECV), transmitter, - num_params, params, &err); - } - - if (fsstream == NULL) { - purple_debug_error("media", - "Error creating stream: %s\n", - err && err->message ? - err->message : "NULL"); - if (err) - g_error_free(err); - g_object_unref(participant); - g_hash_table_remove(media->priv->participants, who); - purple_media_remove_session(media, session); - g_free(session); - return FALSE; - } - - stream = purple_media_insert_stream(session, who, fsstream); - stream->initiator = initiator; - - /* callback for source pad added (new stream source ready) */ - g_signal_connect(G_OBJECT(fsstream), - "src-pad-added", G_CALLBACK(purple_media_src_pad_added_cb), stream); - - g_signal_emit(media, purple_media_signals[STATE_CHANGED], - 0, PURPLE_MEDIA_STATE_NEW, - session->id, who); - } else { - if (purple_media_to_fs_stream_direction(stream->session->type) - != type_direction) { - /* change direction */ - g_object_set(stream->stream, "direction", - type_direction, NULL); - } - } - - 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 - GList *fscodecs; - GList *codecs; - PurpleMediaSession *session; - - g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); - - session = purple_media_get_session(media, sess_id); - - if (session == NULL) - return NULL; - - g_object_get(G_OBJECT(session->session), - "codecs", &fscodecs, NULL); - codecs = purple_media_codec_list_from_fs(fscodecs); - fs_codec_list_destroy(fscodecs); - return codecs; -#else - return NULL; -#endif -} - -GList * -purple_media_get_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 stream ? purple_media_candidate_list_from_fs( - stream->local_candidates) : NULL; -#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; - GError *err = NULL; - - 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_to_fs(remote_candidates)); - - if (stream->initiator == TRUE || stream->accepted == TRUE) { - fs_stream_set_remote_candidates(stream->stream, - stream->remote_candidates, &err); - - if (err) { - purple_debug_error("media", "Error adding remote" - " candidates: %s\n", err->message); - g_error_free(err); - } - } -#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_from_fs( - 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_from_fs( - 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 - PurpleMediaStream *stream; - FsStream *fsstream; - GList *fscodecs; - GError *err = NULL; - - g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); - stream = purple_media_get_stream(media, sess_id, participant); - - if (stream == NULL) - return FALSE; - - fsstream = stream->stream; - fscodecs = purple_media_codec_list_to_fs(codecs); - fs_stream_set_remote_codecs(fsstream, fscodecs, &err); - fs_codec_list_destroy(fscodecs); - - if (err) { - purple_debug_error("media", "Error setting remote codecs: %s\n", - err->message); - g_error_free(err); - return FALSE; - } - return TRUE; -#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 - PurpleMediaSession *session; - FsCodec *fscodec; - GError *err = NULL; - - g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); - - session = purple_media_get_session(media, sess_id); - - if (session != NULL) - return FALSE; - - fscodec = purple_media_codec_to_fs(codec); - fs_session_set_send_codec(session->session, fscodec, &err); - fs_codec_destroy(fscodec); - - if (err) { - purple_debug_error("media", "Error setting send codec\n"); - g_error_free(err); - return FALSE; - } - return TRUE; -#else - return FALSE; -#endif -} - -gboolean -purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id) -{ -#ifdef USE_VV - gboolean ret; - - g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); - - if (sess_id != NULL) { - PurpleMediaSession *session; - session = purple_media_get_session(media, sess_id); - - if (session == NULL) - return FALSE; - if (session->type & (PURPLE_MEDIA_SEND_AUDIO | - PURPLE_MEDIA_SEND_VIDEO)) - g_object_get(session->session, - "codecs-ready", &ret, NULL); - else - ret = TRUE; - } else { - GList *values = g_hash_table_get_values(media->priv->sessions); - for (; values; values = g_list_delete_link(values, values)) { - PurpleMediaSession *session = values->data; - if (session->type & (PURPLE_MEDIA_SEND_AUDIO | - PURPLE_MEDIA_SEND_VIDEO)) - g_object_get(session->session, - "codecs-ready", &ret, NULL); - else - ret = TRUE; - - if (ret == FALSE) - break; - } - if (values != NULL) - g_list_free(values); - } - return ret; -#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 - GList *sessions; - - g_return_if_fail(PURPLE_IS_MEDIA(media)); - - purple_prefs_set_int("/purple/media/audio/volume/input", level); - - if (session_id == NULL) - sessions = g_hash_table_get_values(media->priv->sessions); - else - sessions = g_list_append(NULL, - purple_media_get_session(media, session_id)); - - for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { - PurpleMediaSession *session = sessions->data; - - if (session->type & PURPLE_MEDIA_SEND_AUDIO) { - gchar *name = g_strdup_printf("volume_%s", - session->id); - GstElement *volume = gst_bin_get_by_name( - GST_BIN(session->media->priv->confbin), - name); - g_free(name); - g_object_set(volume, "volume", level/10.0, NULL); - } - } -#endif -} - -void purple_media_set_output_volume(PurpleMedia *media, - const gchar *session_id, const gchar *participant, - double level) -{ -#ifdef USE_VV - GList *streams; - - g_return_if_fail(PURPLE_IS_MEDIA(media)); - - purple_prefs_set_int("/purple/media/audio/volume/output", level); - - streams = purple_media_get_streams(media, - session_id, participant); - - for (; streams; streams = g_list_delete_link(streams, streams)) { - PurpleMediaStream *stream = streams->data; - - if (stream->session->type & PURPLE_MEDIA_RECV_AUDIO - && GST_IS_ELEMENT(stream->volume)) { - g_object_set(stream->volume, "volume", level/10.0, NULL); - } - } -#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 (session_id != NULL && participant == NULL) { - PurpleMediaSession *session = - purple_media_get_session(media, session_id); - return (session != NULL) ? session->tee : NULL; - } else if (session_id != NULL && participant != NULL) { - PurpleMediaStream *stream = - purple_media_get_stream(media, - session_id, participant); - return (stream != NULL) ? stream->tee : NULL; - } - g_return_val_if_reached(NULL); -#else - return NULL; -#endif -} -#endif /* USE_GSTREAMER */ - diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media.h --- a/libpurple/media.h Wed Nov 11 02:47:50 2009 +0000 +++ b/libpurple/media.h Wed Nov 11 03:07:21 2009 +0000 @@ -27,26 +27,15 @@ #ifndef _PURPLE_MEDIA_H_ #define _PURPLE_MEDIA_H_ +#include "media/candidate.h" +#include "media/codec.h" +#include "media/enum-types.h" + #include #include G_BEGIN_DECLS -#define PURPLE_TYPE_MEDIA_CANDIDATE (purple_media_candidate_get_type()) -#define PURPLE_MEDIA_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate)) -#define PURPLE_MEDIA_CANDIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate)) -#define PURPLE_IS_MEDIA_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_CANDIDATE)) -#define PURPLE_IS_MEDIA_CANDIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_CANDIDATE)) -#define PURPLE_MEDIA_CANDIDATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate)) - -#define PURPLE_TYPE_MEDIA_CODEC (purple_media_codec_get_type()) -#define PURPLE_MEDIA_CODEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec)) -#define PURPLE_MEDIA_CODEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec)) -#define PURPLE_IS_MEDIA_CODEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_CODEC)) -#define PURPLE_IS_MEDIA_CODEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_CODEC)) -#define PURPLE_MEDIA_CODEC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec)) - -#define PURPLE_TYPE_MEDIA_SESSION_TYPE (purple_media_session_type_get_type()) #define PURPLE_TYPE_MEDIA (purple_media_get_type()) #define PURPLE_MEDIA(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA, PurpleMedia)) #define PURPLE_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA, PurpleMediaClass)) @@ -54,80 +43,8 @@ #define PURPLE_IS_MEDIA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA)) #define PURPLE_MEDIA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA, PurpleMediaClass)) -#define PURPLE_TYPE_MEDIA_CANDIDATE_TYPE (purple_media_candidate_type_get_type()) -#define PURPLE_TYPE_MEDIA_NETWORK_PROTOCOL (purple_media_network_protocol_get_type()) -#define PURPLE_MEDIA_TYPE_STATE (purple_media_state_changed_get_type()) -#define PURPLE_MEDIA_TYPE_INFO_TYPE (purple_media_info_type_get_type()) -#define PURPLE_MEDIA_TYPE_CAPS (purple_media_caps_get_type()) - /** An opaque structure representing a media call. */ typedef struct _PurpleMedia PurpleMedia; -/** An opaque structure representing a network candidate (IP Address and port pair). */ -typedef struct _PurpleMediaCandidate PurpleMediaCandidate; -/** An opaque structure representing an audio or video codec. */ -typedef struct _PurpleMediaCodec PurpleMediaCodec; - -/** Media caps */ -typedef enum { - PURPLE_MEDIA_CAPS_NONE = 0, - PURPLE_MEDIA_CAPS_AUDIO = 1, - PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION = 1 << 1, - PURPLE_MEDIA_CAPS_VIDEO = 1 << 2, - PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION = 1 << 3, - PURPLE_MEDIA_CAPS_AUDIO_VIDEO = 1 << 4, - PURPLE_MEDIA_CAPS_MODIFY_SESSION = 1 << 5, - PURPLE_MEDIA_CAPS_CHANGE_DIRECTION = 1 << 6, -} PurpleMediaCaps; - -/** Media session types */ -typedef enum { - PURPLE_MEDIA_NONE = 0, - PURPLE_MEDIA_RECV_AUDIO = 1 << 0, - PURPLE_MEDIA_SEND_AUDIO = 1 << 1, - PURPLE_MEDIA_RECV_VIDEO = 1 << 2, - PURPLE_MEDIA_SEND_VIDEO = 1 << 3, - PURPLE_MEDIA_AUDIO = PURPLE_MEDIA_RECV_AUDIO | PURPLE_MEDIA_SEND_AUDIO, - PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO -} PurpleMediaSessionType; - -/** Media state-changed types */ -typedef enum { - PURPLE_MEDIA_STATE_NEW = 0, - PURPLE_MEDIA_STATE_CONNECTED, - PURPLE_MEDIA_STATE_END, -} PurpleMediaState; - -/** Media info types */ -typedef enum { - PURPLE_MEDIA_INFO_HANGUP = 0, - PURPLE_MEDIA_INFO_ACCEPT, - PURPLE_MEDIA_INFO_REJECT, - PURPLE_MEDIA_INFO_MUTE, - PURPLE_MEDIA_INFO_UNMUTE, - PURPLE_MEDIA_INFO_PAUSE, - PURPLE_MEDIA_INFO_UNPAUSE, - PURPLE_MEDIA_INFO_HOLD, - PURPLE_MEDIA_INFO_UNHOLD, -} PurpleMediaInfoType; - -typedef enum { - PURPLE_MEDIA_CANDIDATE_TYPE_HOST, - PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX, - PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX, - PURPLE_MEDIA_CANDIDATE_TYPE_RELAY, - PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST, -} PurpleMediaCandidateType; - -typedef enum { - PURPLE_MEDIA_COMPONENT_NONE = 0, - PURPLE_MEDIA_COMPONENT_RTP = 1, - PURPLE_MEDIA_COMPONENT_RTCP = 2, -} PurpleMediaComponentType; - -typedef enum { - PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, - PURPLE_MEDIA_NETWORK_PROTOCOL_TCP, -} PurpleMediaNetworkProtocol; #include "signals.h" #include "util.h" @@ -137,33 +54,6 @@ #endif /** - * Gets the media session type's GType - * - * @return The media session type's GType. - * - * @since 2.6.0 - */ -GType purple_media_session_type_get_type(void); - -/** - * Gets the media candidate type's GType - * - * @return The media candidate type's GType. - * - * @since 2.6.0 - */ -GType purple_media_candidate_type_get_type(void); - -/** - * Gets the media network protocol's GType - * - * @return The media network protocol's GType. - * - * @since 2.6.0 - */ -GType purple_media_network_protocol_get_type(void); - -/** * Gets the media class's GType * * @return The media class's GType. @@ -173,196 +63,6 @@ GType purple_media_get_type(void); /** - * Gets the type of the state-changed enum - * - * @return The state-changed enum's GType - * - * @since 2.6.0 - */ -GType purple_media_state_changed_get_type(void); - -/** - * Gets the type of the info type enum - * - * @return The info type enum's GType - * - * @since 2.6.0 - */ -GType purple_media_info_type_get_type(void); - -/** - * Gets the type of the media caps flags - * - * @return The media caps flags' GType - * - * @since 2.7.0 - */ -GType purple_media_caps_get_type(void); - -/** - * Gets the type of the media candidate structure. - * - * @return The media canditate's GType - * - * @since 2.6.0 - */ -GType purple_media_candidate_get_type(void); - -/** - * Creates a PurpleMediaCandidate instance. - * - * @param foundation The foundation of the candidate. - * @param component_id The component this candidate is for. - * @param type The type of candidate. - * @param proto The protocol this component is for. - * @param ip The IP address of this component. - * @param port The network port. - * - * @return The newly created PurpleMediaCandidate instance. - * - * @since 2.6.0 - */ -PurpleMediaCandidate *purple_media_candidate_new( - const gchar *foundation, guint component_id, - PurpleMediaCandidateType type, - PurpleMediaNetworkProtocol proto, - const gchar *ip, guint port); - -/** - * Copies a GList of PurpleMediaCandidate and its contents. - * - * @param candidates The list of candidates to be copied. - * - * @return The copy of the GList. - * - * @since 2.6.0 - */ -GList *purple_media_candidate_list_copy(GList *candidates); - -/** - * Frees a GList of PurpleMediaCandidate and its contents. - * - * @param candidates The list of candidates to be freed. - * - * @since 2.6.0 - */ -void purple_media_candidate_list_free(GList *candidates); - -gchar *purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate); -guint purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate); -gchar *purple_media_candidate_get_ip(PurpleMediaCandidate *candidate); -guint16 purple_media_candidate_get_port(PurpleMediaCandidate *candidate); -gchar *purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate); -guint16 purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate); -PurpleMediaNetworkProtocol purple_media_candidate_get_protocol( - PurpleMediaCandidate *candidate); -guint32 purple_media_candidate_get_priority(PurpleMediaCandidate *candidate); -PurpleMediaCandidateType purple_media_candidate_get_candidate_type( - PurpleMediaCandidate *candidate); -gchar *purple_media_candidate_get_username(PurpleMediaCandidate *candidate); -gchar *purple_media_candidate_get_password(PurpleMediaCandidate *candidate); -guint purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate); - -/** - * Gets the type of the media codec structure. - * - * @return The media codec's GType - * - * @since 2.6.0 - */ -GType purple_media_codec_get_type(void); - -/** - * Creates a new PurpleMediaCodec instance. - * - * @param id Codec identifier. - * @param encoding_name Name of the media type this encodes. - * @param media_type PurpleMediaSessionType of this codec. - * @param clock_rate The clock rate this codec encodes at, if applicable. - * - * @return The newly created PurpleMediaCodec. - * - * @since 2.6.0 - */ -PurpleMediaCodec *purple_media_codec_new(int id, const char *encoding_name, - PurpleMediaSessionType media_type, guint clock_rate); - -guint purple_media_codec_get_id(PurpleMediaCodec *codec); -gchar *purple_media_codec_get_encoding_name(PurpleMediaCodec *codec); -guint purple_media_codec_get_clock_rate(PurpleMediaCodec *codec); -guint purple_media_codec_get_channels(PurpleMediaCodec *codec); -GList *purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec); - -/** - * Creates a string representation of the codec. - * - * @param codec The codec to create the string of. - * - * @return The new string representation. - * - * @since 2.6.0 - */ -gchar *purple_media_codec_to_string(const PurpleMediaCodec *codec); - -/** - * Adds an optional parameter to the codec. - * - * @param codec The codec to add the parameter to. - * @param name The name of the parameter to add. - * @param value The value of the parameter to add. - * - * @since 2.6.0 - */ -void purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec, - const gchar *name, const gchar *value); - -/** - * Removes an optional parameter from the codec. - * - * @param codec The codec to remove the parameter from. - * @param param A pointer to the parameter to remove. - * - * @since 2.6.0 - */ -void purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec, - PurpleKeyValuePair *param); - -/** - * Gets an optional parameter based on the values given. - * - * @param codec The codec to find the parameter in. - * @param name The name of the parameter to search for. - * @param value The value to search for or NULL. - * - * @return The value found or NULL. - * - * @since 2.6.0 - */ -PurpleKeyValuePair *purple_media_codec_get_optional_parameter( - PurpleMediaCodec *codec, const gchar *name, - const gchar *value); - -/** - * Copies a GList of PurpleMediaCodec and its contents. - * - * @param codecs The list of codecs to be copied. - * - * @return The copy of the GList. - * - * @since 2.6.0 - */ -GList *purple_media_codec_list_copy(GList *codecs); - -/** - * Frees a GList of PurpleMediaCodec and its contents. - * - * @param codecs The list of codecs to be freed. - * - * @since 2.6.0 - */ -void purple_media_codec_list_free(GList *codecs); - -/** * Gets a list of session IDs. * * @param media The media session from which to retrieve session IDs. diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media/backend-fs2.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/backend-fs2.c Wed Nov 11 03:07:21 2009 +0000 @@ -0,0 +1,2009 @@ +/** + * @file backend-fs2.c Farsight 2 backend for 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 "backend-fs2.h" + +#include "internal.h" + +#include "backend-iface.h" +#include "debug.h" +#include "network.h" +#include "media-gst.h" + +#include +#include + +/** @copydoc _PurpleMediaBackendFs2Class */ +typedef struct _PurpleMediaBackendFs2Class PurpleMediaBackendFs2Class; +/** @copydoc _PurpleMediaBackendFs2Private */ +typedef struct _PurpleMediaBackendFs2Private PurpleMediaBackendFs2Private; +/** @copydoc _PurpleMediaBackendFs2Session */ +typedef struct _PurpleMediaBackendFs2Session PurpleMediaBackendFs2Session; +/** @copydoc _PurpleMediaBackendFs2Stream */ +typedef struct _PurpleMediaBackendFs2Stream PurpleMediaBackendFs2Stream; + +#define PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), \ + PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2Private)) + +static void purple_media_backend_iface_init(PurpleMediaBackendIface *iface); + +static gboolean +gst_bus_cb(GstBus *bus, GstMessage *msg, PurpleMediaBackendFs2 *self); +static void +state_changed_cb(PurpleMedia *media, PurpleMediaState state, + gchar *sid, gchar *name, PurpleMediaBackendFs2 *self); +static void +stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, + gchar *sid, gchar *name, gboolean local, + PurpleMediaBackendFs2 *self); + +static gboolean purple_media_backend_fs2_add_stream(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *who, + PurpleMediaSessionType type, gboolean initiator, + const gchar *transmitter, + guint num_params, GParameter *params); +static void purple_media_backend_fs2_add_remote_candidates( + PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant, + GList *remote_candidates); +static gboolean purple_media_backend_fs2_codecs_ready(PurpleMediaBackend *self, + const gchar *sess_id); +static GList *purple_media_backend_fs2_get_codecs(PurpleMediaBackend *self, + const gchar *sess_id); +static GList *purple_media_backend_fs2_get_local_candidates( + PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant); +static gboolean purple_media_backend_fs2_set_remote_codecs( + PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant, + GList *codecs); +static gboolean purple_media_backend_fs2_set_send_codec( + PurpleMediaBackend *self, const gchar *sess_id, + PurpleMediaCodec *codec); + +struct _PurpleMediaBackendFs2Class +{ + GObjectClass parent_class; +}; + +struct _PurpleMediaBackendFs2 +{ + GObject parent; +}; + +G_DEFINE_TYPE_WITH_CODE(PurpleMediaBackendFs2, purple_media_backend_fs2, + G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE( + PURPLE_TYPE_MEDIA_BACKEND, purple_media_backend_iface_init)); + +struct _PurpleMediaBackendFs2Stream +{ + PurpleMediaBackendFs2Session *session; + gchar *participant; + FsStream *stream; + + GstElement *src; + GstElement *tee; + GstElement *volume; + GstElement *level; + + GList *local_candidates; + GList *remote_candidates; + + guint connected_cb_id; +}; + +struct _PurpleMediaBackendFs2Session +{ + PurpleMediaBackendFs2 *backend; + gchar *id; + FsSession *session; + + GstElement *src; + GstElement *tee; + + PurpleMediaSessionType type; +}; + +struct _PurpleMediaBackendFs2Private +{ + PurpleMedia *media; + GstElement *confbin; + FsConference *conference; + gchar *conference_type; + + GHashTable *sessions; + GHashTable *participants; + + GList *streams; +}; + +enum { + PROP_0, + PROP_CONFERENCE_TYPE, + PROP_MEDIA, +}; + +static void +purple_media_backend_fs2_init(PurpleMediaBackendFs2 *self) +{ +} + +static void +purple_media_backend_fs2_dispose(GObject *obj) +{ + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj); + GList *iter = NULL; + + purple_debug_info("backend-fs2", "purple_media_backend_fs2_dispose\n"); + + if (priv->confbin) { + GstElement *pipeline; + + pipeline = purple_media_manager_get_pipeline( + purple_media_get_manager(priv->media)); + + gst_element_set_locked_state(priv->confbin, TRUE); + gst_element_set_state(GST_ELEMENT(priv->confbin), + GST_STATE_NULL); + + if (pipeline) { + GstBus *bus; + gst_bin_remove(GST_BIN(pipeline), priv->confbin); + bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); + g_signal_handlers_disconnect_matched(G_OBJECT(bus), + G_SIGNAL_MATCH_FUNC | + G_SIGNAL_MATCH_DATA, + 0, 0, 0, gst_bus_cb, obj); + gst_object_unref(bus); + } else { + purple_debug_warning("backend-fs2", "Unable to " + "properly dispose the conference. " + "Couldn't get the pipeline.\n"); + } + + priv->confbin = NULL; + priv->conference = NULL; + + } + + if (priv->sessions) { + GList *sessions = g_hash_table_get_values(priv->sessions); + + for (; sessions; sessions = + g_list_delete_link(sessions, sessions)) { + PurpleMediaBackendFs2Session *session = + sessions->data; + + if (session->session) { + g_object_unref(session->session); + session->session = NULL; + } + } + } + + if (priv->participants) { + GList *participants = + g_hash_table_get_values(priv->participants); + for (; participants; participants = g_list_delete_link( + participants, participants)) + g_object_unref(participants->data); + priv->participants = NULL; + } + + for (iter = priv->streams; iter; iter = g_list_next(iter)) { + PurpleMediaBackendFs2Stream *stream = iter->data; + if (stream->stream) { + g_object_unref(stream->stream); + stream->stream = NULL; + } + } + + if (priv->media) { + g_object_remove_weak_pointer(G_OBJECT(priv->media), + (gpointer*)&priv->media); + priv->media = NULL; + } + + G_OBJECT_CLASS(purple_media_backend_fs2_parent_class)->dispose(obj); +} + +static void +purple_media_backend_fs2_finalize(GObject *obj) +{ + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj); + + purple_debug_info("backend-fs2", "purple_media_backend_fs2_finalize\n"); + + g_free(priv->conference_type); + + for (; priv->streams; priv->streams = + g_list_delete_link(priv->streams, priv->streams)) { + PurpleMediaBackendFs2Stream *stream = priv->streams->data; + + /* Remove the connected_cb timeout */ + if (stream->connected_cb_id != 0) + purple_timeout_remove(stream->connected_cb_id); + + g_free(stream->participant); + + if (stream->local_candidates) + fs_candidate_list_destroy(stream->local_candidates); + + if (stream->remote_candidates) + fs_candidate_list_destroy(stream->remote_candidates); + + g_free(stream); + } + + if (priv->sessions) { + GList *sessions = g_hash_table_get_values(priv->sessions); + + for (; sessions; sessions = + g_list_delete_link(sessions, sessions)) { + PurpleMediaBackendFs2Session *session = + sessions->data; + g_free(session->id); + g_free(session); + } + + g_hash_table_destroy(priv->sessions); + } + + G_OBJECT_CLASS(purple_media_backend_fs2_parent_class)->finalize(obj); +} + +static void +purple_media_backend_fs2_set_property(GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + PurpleMediaBackendFs2Private *priv; + g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object)); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_CONFERENCE_TYPE: + priv->conference_type = g_value_dup_string(value); + break; + case PROP_MEDIA: + priv->media = g_value_get_object(value); + + if (priv->media == NULL) + break; + + g_object_add_weak_pointer(G_OBJECT(priv->media), + (gpointer*)&priv->media); + + g_signal_connect(G_OBJECT(priv->media), + "state-changed", + G_CALLBACK(state_changed_cb), + PURPLE_MEDIA_BACKEND_FS2(object)); + g_signal_connect(G_OBJECT(priv->media), "stream-info", + G_CALLBACK(stream_info_cb), + PURPLE_MEDIA_BACKEND_FS2(object)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( + object, prop_id, pspec); + break; + } +} + +static void +purple_media_backend_fs2_get_property(GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + PurpleMediaBackendFs2Private *priv; + g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object)); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_CONFERENCE_TYPE: + g_value_set_string(value, priv->conference_type); + break; + case PROP_MEDIA: + g_value_set_object(value, priv->media); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( + object, prop_id, pspec); + break; + } +} + +static void +purple_media_backend_fs2_class_init(PurpleMediaBackendFs2Class *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + + gobject_class->dispose = purple_media_backend_fs2_dispose; + gobject_class->finalize = purple_media_backend_fs2_finalize; + gobject_class->set_property = purple_media_backend_fs2_set_property; + gobject_class->get_property = purple_media_backend_fs2_get_property; + + g_object_class_override_property(gobject_class, PROP_CONFERENCE_TYPE, + "conference-type"); + g_object_class_override_property(gobject_class, PROP_MEDIA, "media"); + + g_type_class_add_private(klass, sizeof(PurpleMediaBackendFs2Private)); +} + +static void +purple_media_backend_iface_init(PurpleMediaBackendIface *iface) +{ + iface->add_stream = purple_media_backend_fs2_add_stream; + iface->add_remote_candidates = + purple_media_backend_fs2_add_remote_candidates; + iface->codecs_ready = purple_media_backend_fs2_codecs_ready; + iface->get_codecs = purple_media_backend_fs2_get_codecs; + iface->get_local_candidates = + purple_media_backend_fs2_get_local_candidates; + iface->set_remote_codecs = purple_media_backend_fs2_set_remote_codecs; + iface->set_send_codec = purple_media_backend_fs2_set_send_codec; +} + +static FsMediaType +session_type_to_fs_media_type(PurpleMediaSessionType type) +{ + if (type & PURPLE_MEDIA_AUDIO) + return FS_MEDIA_TYPE_AUDIO; + else if (type & PURPLE_MEDIA_VIDEO) + return FS_MEDIA_TYPE_VIDEO; + else + return 0; +} + +static FsStreamDirection +session_type_to_fs_stream_direction(PurpleMediaSessionType type) +{ + if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_AUDIO || + (type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO) + return FS_DIRECTION_BOTH; + else if ((type & PURPLE_MEDIA_SEND_AUDIO) || + (type & PURPLE_MEDIA_SEND_VIDEO)) + return FS_DIRECTION_SEND; + else if ((type & PURPLE_MEDIA_RECV_AUDIO) || + (type & PURPLE_MEDIA_RECV_VIDEO)) + return FS_DIRECTION_RECV; + else + return FS_DIRECTION_NONE; +} + +static PurpleMediaSessionType +session_type_from_fs(FsMediaType type, FsStreamDirection direction) +{ + PurpleMediaSessionType result = PURPLE_MEDIA_NONE; + if (type == FS_MEDIA_TYPE_AUDIO) { + if (direction & FS_DIRECTION_SEND) + result |= PURPLE_MEDIA_SEND_AUDIO; + if (direction & FS_DIRECTION_RECV) + result |= PURPLE_MEDIA_RECV_AUDIO; + } else if (type == FS_MEDIA_TYPE_VIDEO) { + if (direction & FS_DIRECTION_SEND) + result |= PURPLE_MEDIA_SEND_VIDEO; + if (direction & FS_DIRECTION_RECV) + result |= PURPLE_MEDIA_RECV_VIDEO; + } + return result; +} + +static FsCandidate * +candidate_to_fs(PurpleMediaCandidate *candidate) +{ + FsCandidate *fscandidate; + gchar *foundation; + guint component_id; + gchar *ip; + guint port; + gchar *base_ip; + guint base_port; + PurpleMediaNetworkProtocol proto; + guint32 priority; + PurpleMediaCandidateType type; + gchar *username; + gchar *password; + guint ttl; + + if (candidate == NULL) + return NULL; + + g_object_get(G_OBJECT(candidate), + "foundation", &foundation, + "component-id", &component_id, + "ip", &ip, + "port", &port, + "base-ip", &base_ip, + "base-port", &base_port, + "protocol", &proto, + "priority", &priority, + "type", &type, + "username", &username, + "password", &password, + "ttl", &ttl, + NULL); + + fscandidate = fs_candidate_new(foundation, + component_id, type, + proto, ip, port); + + fscandidate->base_ip = base_ip; + fscandidate->base_port = base_port; + fscandidate->priority = priority; + fscandidate->username = username; + fscandidate->password = password; + fscandidate->ttl = ttl; + + g_free(foundation); + g_free(ip); + return fscandidate; +} + +static GList * +candidate_list_to_fs(GList *candidates) +{ + GList *new_list = NULL; + + for (; candidates; candidates = g_list_next(candidates)) { + new_list = g_list_prepend(new_list, + candidate_to_fs(candidates->data)); + } + + new_list = g_list_reverse(new_list); + return new_list; +} + +static PurpleMediaCandidate * +candidate_from_fs(FsCandidate *fscandidate) +{ + PurpleMediaCandidate *candidate; + + if (fscandidate == NULL) + return NULL; + + candidate = purple_media_candidate_new(fscandidate->foundation, + fscandidate->component_id, fscandidate->type, + fscandidate->proto, fscandidate->ip, fscandidate->port); + g_object_set(candidate, + "base-ip", fscandidate->base_ip, + "base-port", fscandidate->base_port, + "priority", fscandidate->priority, + "username", fscandidate->username, + "password", fscandidate->password, + "ttl", fscandidate->ttl, NULL); + return candidate; +} + +static GList * +candidate_list_from_fs(GList *candidates) +{ + GList *new_list = NULL; + + for (; candidates; candidates = g_list_next(candidates)) { + new_list = g_list_prepend(new_list, + candidate_from_fs(candidates->data)); + } + + new_list = g_list_reverse(new_list); + return new_list; +} + +static FsCodec * +codec_to_fs(const PurpleMediaCodec *codec) +{ + FsCodec *new_codec; + gint id; + char *encoding_name; + PurpleMediaSessionType media_type; + guint clock_rate; + guint channels; + GList *iter; + + if (codec == NULL) + return NULL; + + g_object_get(G_OBJECT(codec), + "id", &id, + "encoding-name", &encoding_name, + "media-type", &media_type, + "clock-rate", &clock_rate, + "channels", &channels, + "optional-params", &iter, + NULL); + + new_codec = fs_codec_new(id, encoding_name, + session_type_to_fs_media_type(media_type), + clock_rate); + new_codec->channels = channels; + + for (; iter; iter = g_list_next(iter)) { + PurpleKeyValuePair *param = (PurpleKeyValuePair*)iter->data; + fs_codec_add_optional_parameter(new_codec, + param->key, param->value); + } + + g_free(encoding_name); + return new_codec; +} + +static PurpleMediaCodec * +codec_from_fs(const FsCodec *codec) +{ + PurpleMediaCodec *new_codec; + GList *iter; + + if (codec == NULL) + return NULL; + + new_codec = purple_media_codec_new(codec->id, codec->encoding_name, + session_type_from_fs(codec->media_type, + FS_DIRECTION_BOTH), codec->clock_rate); + g_object_set(new_codec, "channels", codec->channels, NULL); + + for (iter = codec->optional_params; iter; iter = g_list_next(iter)) { + FsCodecParameter *param = (FsCodecParameter*)iter->data; + purple_media_codec_add_optional_parameter(new_codec, + param->name, param->value); + } + + return new_codec; +} + +static GList * +codec_list_from_fs(GList *codecs) +{ + GList *new_list = NULL; + + for (; codecs; codecs = g_list_next(codecs)) { + new_list = g_list_prepend(new_list, + codec_from_fs(codecs->data)); + } + + new_list = g_list_reverse(new_list); + return new_list; +} + +static GList * +codec_list_to_fs(GList *codecs) +{ + GList *new_list = NULL; + + for (; codecs; codecs = g_list_next(codecs)) { + new_list = g_list_prepend(new_list, + codec_to_fs(codecs->data)); + } + + new_list = g_list_reverse(new_list); + return new_list; +} + +static PurpleMediaBackendFs2Session * +get_session(PurpleMediaBackendFs2 *self, const gchar *sess_id) +{ + PurpleMediaBackendFs2Private *priv; + PurpleMediaBackendFs2Session *session = NULL; + + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + + if (priv->sessions != NULL) + session = g_hash_table_lookup(priv->sessions, sess_id); + + return session; +} + +static FsParticipant * +get_participant(PurpleMediaBackendFs2 *self, const gchar *name) +{ + PurpleMediaBackendFs2Private *priv; + FsParticipant *participant = NULL; + + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + + if (priv->participants != NULL) + participant = g_hash_table_lookup(priv->participants, name); + + return participant; +} + +static PurpleMediaBackendFs2Stream * +get_stream(PurpleMediaBackendFs2 *self, + const gchar *sess_id, const gchar *name) +{ + PurpleMediaBackendFs2Private *priv; + GList *streams; + + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + streams = priv->streams; + + for (; streams; streams = g_list_next(streams)) { + PurpleMediaBackendFs2Stream *stream = streams->data; + if (!strcmp(stream->session->id, sess_id) && + !strcmp(stream->participant, name)) + return stream; + } + + return NULL; +} + +static GList * +get_streams(PurpleMediaBackendFs2 *self, + const gchar *sess_id, const gchar *name) +{ + PurpleMediaBackendFs2Private *priv; + GList *streams, *ret = NULL; + + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + streams = priv->streams; + + for (; streams; streams = g_list_next(streams)) { + PurpleMediaBackendFs2Stream *stream = streams->data; + + if (sess_id != NULL && strcmp(stream->session->id, sess_id)) + continue; + else if (name != NULL && strcmp(stream->participant, name)) + continue; + else + ret = g_list_prepend(ret, stream); + } + + ret = g_list_reverse(ret); + return ret; +} + +static PurpleMediaBackendFs2Session * +get_session_from_fs_stream(PurpleMediaBackendFs2 *self, FsStream *stream) +{ + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + FsSession *fssession; + GList *values; + + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); + g_return_val_if_fail(FS_IS_STREAM(stream), NULL); + + g_object_get(stream, "session", &fssession, NULL); + + values = g_hash_table_get_values(priv->sessions); + + for (; values; values = g_list_delete_link(values, values)) { + PurpleMediaBackendFs2Session *session = values->data; + + if (session->session == fssession) { + g_list_free(values); + g_object_unref(fssession); + return session; + } + } + + g_object_unref(fssession); + return NULL; +} + +static void +gst_handle_message_element(GstBus *bus, GstMessage *msg, + PurpleMediaBackendFs2 *self) +{ + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); + static guint level_id = 0; + + if (level_id == 0) + level_id = g_signal_lookup("level", PURPLE_TYPE_MEDIA); + + if (g_signal_has_handler_pending(priv->media, level_id, 0, FALSE) + && gst_structure_has_name( + gst_message_get_structure(msg), "level")) { + GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); + gchar *name; + gchar *participant = NULL; + PurpleMediaBackendFs2Session *session = NULL; + gdouble rms_db; + gdouble percent; + const GValue *list; + const GValue *value; + + if (!PURPLE_IS_MEDIA(priv->media) || + GST_ELEMENT_PARENT(src) != priv->confbin) + return; + + name = gst_element_get_name(src); + + if (!strncmp(name, "sendlevel_", 10)) { + session = get_session(self, name+10); + } else { + GList *iter = priv->streams; + PurpleMediaBackendFs2Stream *stream; + for (; iter; iter = g_list_next(iter)) { + stream = iter->data; + if (stream->level == src) { + session = stream->session; + participant = stream->participant; + break; + } + } + } + + g_free(name); + + if (!session) + return; + + list = gst_structure_get_value( + gst_message_get_structure(msg), "rms"); + value = gst_value_list_get_value(list, 0); + rms_db = g_value_get_double(value); + percent = pow(10, rms_db / 20) * 5; + + if(percent > 1.0) + percent = 1.0; + + g_signal_emit(priv->media, level_id, 0, + session->id, participant, percent); + return; + } + + if (!FS_IS_CONFERENCE(src) || !PURPLE_IS_MEDIA_BACKEND(self) || + priv->conference != FS_CONFERENCE(src)) + return; + + if (gst_structure_has_name(msg->structure, "farsight-error")) { + FsError error_no; + gst_structure_get_enum(msg->structure, "error-no", + FS_TYPE_ERROR, (gint*)&error_no); + switch (error_no) { + case FS_ERROR_NO_CODECS: + purple_media_error(priv->media, _("No codecs" + " found. Install some" + " GStreamer codecs found" + " in GStreamer plugins" + " packages.")); + purple_media_end(priv->media, NULL, NULL); + break; + case FS_ERROR_NO_CODECS_LEFT: + purple_media_error(priv->media, _("No codecs" + " left. Your codec" + " preferences in" + " fs-codecs.conf are too" + " strict.")); + purple_media_end(priv->media, NULL, NULL); + break; + case FS_ERROR_UNKNOWN_CNAME: + /* + * Unknown CName is only a problem for the + * multicast transmitter which isn't used. + * It is also deprecated. + */ + break; + default: + purple_debug_error("backend-fs2", + "farsight-error: %i: %s\n", + error_no, + gst_structure_get_string( + msg->structure, "error-msg")); + break; + } + + if (FS_ERROR_IS_FATAL(error_no)) { + purple_media_error(priv->media, _("A non-recoverable " + "Farsight2 error has occurred.")); + purple_media_end(priv->media, NULL, NULL); + } + } else if (gst_structure_has_name(msg->structure, + "farsight-new-local-candidate")) { + const GValue *value; + FsStream *stream; + FsCandidate *local_candidate; + PurpleMediaCandidate *candidate; + FsParticipant *participant; + PurpleMediaBackendFs2Session *session; + PurpleMediaBackendFs2Stream *media_stream; + gchar *name; + + value = gst_structure_get_value(msg->structure, "stream"); + stream = g_value_get_object(value); + value = gst_structure_get_value(msg->structure, "candidate"); + local_candidate = g_value_get_boxed(value); + + session = get_session_from_fs_stream(self, stream); + + purple_debug_info("backend-fs2", + "got new local candidate: %s\n", + local_candidate->foundation); + + g_object_get(stream, "participant", &participant, NULL); + g_object_get(participant, "cname", &name, NULL); + g_object_unref(participant); + + media_stream = get_stream(self, session->id, name); + media_stream->local_candidates = g_list_append( + media_stream->local_candidates, + fs_candidate_copy(local_candidate)); + + candidate = candidate_from_fs(local_candidate); + g_signal_emit_by_name(self, "new-candidate", + session->id, name, candidate); + g_object_unref(candidate); + } else if (gst_structure_has_name(msg->structure, + "farsight-local-candidates-prepared")) { + const GValue *value; + FsStream *stream; + FsParticipant *participant; + PurpleMediaBackendFs2Session *session; + gchar *name; + + value = gst_structure_get_value(msg->structure, "stream"); + stream = g_value_get_object(value); + session = get_session_from_fs_stream(self, stream); + + g_object_get(stream, "participant", &participant, NULL); + g_object_get(participant, "cname", &name, NULL); + g_object_unref(participant); + + g_signal_emit_by_name(self, "candidates-prepared", + session->id, name); + } else if (gst_structure_has_name(msg->structure, + "farsight-new-active-candidate-pair")) { + const GValue *value; + FsStream *stream; + FsCandidate *local_candidate; + FsCandidate *remote_candidate; + FsParticipant *participant; + PurpleMediaBackendFs2Session *session; + PurpleMediaCandidate *lcandidate, *rcandidate; + gchar *name; + + value = gst_structure_get_value(msg->structure, "stream"); + stream = g_value_get_object(value); + value = gst_structure_get_value(msg->structure, + "local-candidate"); + local_candidate = g_value_get_boxed(value); + value = gst_structure_get_value(msg->structure, + "remote-candidate"); + remote_candidate = g_value_get_boxed(value); + + g_object_get(stream, "participant", &participant, NULL); + g_object_get(participant, "cname", &name, NULL); + g_object_unref(participant); + + session = get_session_from_fs_stream(self, stream); + + lcandidate = candidate_from_fs(local_candidate); + rcandidate = candidate_from_fs(remote_candidate); + + g_signal_emit_by_name(self, "active-candidate-pair", + session->id, name, lcandidate, rcandidate); + + g_object_unref(lcandidate); + g_object_unref(rcandidate); + } else if (gst_structure_has_name(msg->structure, + "farsight-recv-codecs-changed")) { + const GValue *value; + GList *codecs; + FsCodec *codec; + + value = gst_structure_get_value(msg->structure, "codecs"); + codecs = g_value_get_boxed(value); + codec = codecs->data; + + purple_debug_info("backend-fs2", + "farsight-recv-codecs-changed: %s\n", + codec->encoding_name); + } else if (gst_structure_has_name(msg->structure, + "farsight-component-state-changed")) { + const GValue *value; + FsStreamState fsstate; + guint component; + const gchar *state; + + value = gst_structure_get_value(msg->structure, "state"); + fsstate = g_value_get_enum(value); + value = gst_structure_get_value(msg->structure, "component"); + component = g_value_get_uint(value); + + switch (fsstate) { + case FS_STREAM_STATE_FAILED: + state = "FAILED"; + break; + case FS_STREAM_STATE_DISCONNECTED: + state = "DISCONNECTED"; + break; + case FS_STREAM_STATE_GATHERING: + state = "GATHERING"; + break; + case FS_STREAM_STATE_CONNECTING: + state = "CONNECTING"; + break; + case FS_STREAM_STATE_CONNECTED: + state = "CONNECTED"; + break; + case FS_STREAM_STATE_READY: + state = "READY"; + break; + default: + state = "UNKNOWN"; + break; + } + + purple_debug_info("backend-fs2", + "farsight-component-state-changed: " + "component: %u state: %s\n", + component, state); + } else if (gst_structure_has_name(msg->structure, + "farsight-send-codec-changed")) { + const GValue *value; + FsCodec *codec; + gchar *codec_str; + + value = gst_structure_get_value(msg->structure, "codec"); + codec = g_value_get_boxed(value); + codec_str = fs_codec_to_string(codec); + + purple_debug_info("backend-fs2", + "farsight-send-codec-changed: codec: %s\n", + codec_str); + + g_free(codec_str); + } else if (gst_structure_has_name(msg->structure, + "farsight-codecs-changed")) { + const GValue *value; + FsSession *fssession; + GList *sessions; + + value = gst_structure_get_value(msg->structure, "session"); + fssession = g_value_get_object(value); + sessions = g_hash_table_get_values(priv->sessions); + + for (; sessions; sessions = + g_list_delete_link(sessions, sessions)) { + PurpleMediaBackendFs2Session *session = sessions->data; + gchar *session_id; + + if (session->session != fssession) + continue; + + session_id = g_strdup(session->id); + g_signal_emit_by_name(self, "codecs-changed", + session_id); + g_free(session_id); + g_list_free(sessions); + break; + } + } +} + +static void +gst_handle_message_error(GstBus *bus, GstMessage *msg, + PurpleMediaBackendFs2 *self) +{ + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(msg)); + GstElement *lastElement = NULL; + GList *sessions; + + while (!GST_IS_PIPELINE(element)) { + if (element == priv->confbin) + break; + + lastElement = element; + element = GST_ELEMENT_PARENT(element); + } + + if (!GST_IS_PIPELINE(element)) + return; + + sessions = purple_media_get_session_ids(priv->media); + + for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { + if (purple_media_get_src(priv->media, sessions->data) + != lastElement) + continue; + + if (purple_media_get_session_type(priv->media, sessions->data) + & PURPLE_MEDIA_AUDIO) + purple_media_error(priv->media, + _("Error with your microphone")); + else + purple_media_error(priv->media, + _("Error with your webcam")); + + break; + } + + g_list_free(sessions); + + purple_media_error(priv->media, _("Conference error")); + purple_media_end(priv->media, NULL, NULL); +} + +static gboolean +gst_bus_cb(GstBus *bus, GstMessage *msg, PurpleMediaBackendFs2 *self) +{ + switch(GST_MESSAGE_TYPE(msg)) { + case GST_MESSAGE_ELEMENT: + gst_handle_message_element(bus, msg, self); + break; + case GST_MESSAGE_ERROR: + gst_handle_message_error(bus, msg, self); + break; + default: + break; + } + + return TRUE; +} + +static void +state_changed_cb(PurpleMedia *media, PurpleMediaState state, + gchar *sid, gchar *name, PurpleMediaBackendFs2 *self) +{ +} + +static void +stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, + gchar *sid, gchar *name, gboolean local, + PurpleMediaBackendFs2 *self) +{ + if (type == PURPLE_MEDIA_INFO_ACCEPT && sid != NULL && name != NULL) { + PurpleMediaBackendFs2Stream *stream = + get_stream(self, sid, name); + GError *err = NULL; + + g_object_set(G_OBJECT(stream->stream), "direction", + session_type_to_fs_stream_direction( + stream->session->type), NULL); + + if (stream->remote_candidates == NULL) + return; + + fs_stream_set_remote_candidates(stream->stream, + stream->remote_candidates, &err); + + if (err == NULL) + return; + + purple_debug_error("backend-fs2", "Error adding " + "remote candidates: %s\n", + err->message); + g_error_free(err); + } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_MUTE || + type == PURPLE_MEDIA_INFO_UNMUTE)) { + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + gboolean active = (type == PURPLE_MEDIA_INFO_MUTE); + GList *sessions; + + if (sid == NULL) + sessions = g_hash_table_get_values(priv->sessions); + else + sessions = g_list_prepend(NULL, + get_session(self, sid)); + + purple_debug_info("media", "Turning mute %s\n", + active ? "on" : "off"); + + for (; sessions; sessions = g_list_delete_link( + sessions, sessions)) { + PurpleMediaBackendFs2Session *session = + sessions->data; + + if (session->type & PURPLE_MEDIA_SEND_AUDIO) { + gchar *name = g_strdup_printf("volume_%s", + session->id); + GstElement *volume = gst_bin_get_by_name( + GST_BIN(priv->confbin), name); + g_free(name); + g_object_set(volume, "mute", active, NULL); + } + } + } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_PAUSE || + type == PURPLE_MEDIA_INFO_UNPAUSE)) { + gboolean active = (type == PURPLE_MEDIA_INFO_PAUSE); + GList *streams = get_streams(self, sid, name); + for (; streams; streams = + g_list_delete_link(streams, streams)) { + PurpleMediaBackendFs2Stream *stream = streams->data; + if (stream->session->type & PURPLE_MEDIA_SEND_VIDEO) { + g_object_set(stream->stream, "direction", + session_type_to_fs_stream_direction( + stream->session->type & ((active) ? + ~PURPLE_MEDIA_SEND_VIDEO : + PURPLE_MEDIA_VIDEO)), NULL); + } + } + } +} + +static gboolean +init_conference(PurpleMediaBackendFs2 *self) +{ + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + GstElement *pipeline; + GstBus *bus; + gchar *name; + + priv->conference = FS_CONFERENCE( + gst_element_factory_make(priv->conference_type, NULL)); + + if (priv->conference == NULL) { + purple_debug_error("backend-fs2", "Conference == NULL\n"); + return FALSE; + } + + pipeline = purple_media_manager_get_pipeline( + purple_media_get_manager(priv->media)); + + if (pipeline == NULL) { + purple_debug_error("backend-fs2", + "Couldn't retrieve pipeline.\n"); + return FALSE; + } + + name = g_strdup_printf("conf_%p", priv->conference); + priv->confbin = gst_bin_new(name); + if (priv->confbin == NULL) { + purple_debug_error("backend-fs2", + "Couldn't create confbin.\n"); + return FALSE; + } + + g_free(name); + + bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); + if (bus == NULL) { + purple_debug_error("backend-fs2", + "Couldn't get the pipeline's bus.\n"); + return FALSE; + } + + g_signal_connect(G_OBJECT(bus), "message", + G_CALLBACK(gst_bus_cb), self); + gst_object_unref(bus); + + if (!gst_bin_add(GST_BIN(pipeline), + GST_ELEMENT(priv->confbin))) { + purple_debug_error("backend-fs2", "Couldn't add confbin " + "element to the pipeline\n"); + return FALSE; + } + + if (!gst_bin_add(GST_BIN(priv->confbin), + GST_ELEMENT(priv->conference))) { + purple_debug_error("backend-fs2", "Couldn't add conference " + "element to the confbin\n"); + return FALSE; + } + + if (gst_element_set_state(GST_ELEMENT(priv->confbin), + GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + purple_debug_error("backend-fs2", + "Failed to start conference.\n"); + return FALSE; + } + + return TRUE; +} + +static void +gst_element_added_cb(FsElementAddedNotifier *self, + GstBin *bin, GstElement *element, gpointer user_data) +{ + /* + * Hack to make H264 work with Gmail video. + */ + if (!strncmp(GST_ELEMENT_NAME(element), "x264", 4)) { + g_object_set(GST_OBJECT(element), "cabac", FALSE, NULL); + } +} + +static gboolean +create_src(PurpleMediaBackendFs2 *self, const gchar *sess_id, + PurpleMediaSessionType type) +{ + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + PurpleMediaBackendFs2Session *session; + PurpleMediaSessionType session_type; + FsMediaType media_type = session_type_to_fs_media_type(type); + FsStreamDirection type_direction = + session_type_to_fs_stream_direction(type); + GstElement *src; + GstPad *sinkpad, *srcpad; + + if ((type_direction & FS_DIRECTION_SEND) == 0) + return TRUE; + + session_type = session_type_from_fs( + media_type, FS_DIRECTION_SEND); + src = purple_media_manager_get_element( + purple_media_get_manager(priv->media), + session_type, priv->media, sess_id, NULL); + + if (!GST_IS_ELEMENT(src)) { + purple_debug_error("backend-fs2", + "Error creating src for session %s\n", + sess_id); + return FALSE; + } + + session = get_session(self, sess_id); + + if (session == NULL) { + purple_debug_warning("backend-fs2", + "purple_media_set_src: trying to set" + " src on non-existent session\n"); + return FALSE; + } + + if (session->src) + gst_object_unref(session->src); + + session->src = src; + gst_element_set_locked_state(session->src, TRUE); + + session->tee = gst_element_factory_make("tee", NULL); + gst_bin_add(GST_BIN(priv->confbin), session->tee); + + /* This supposedly isn't necessary, but it silences some warnings */ + if (GST_ELEMENT_PARENT(priv->confbin) + == GST_ELEMENT_PARENT(session->src)) { + GstPad *pad = gst_element_get_static_pad(session->tee, "sink"); + GstPad *ghost = gst_ghost_pad_new(NULL, pad); + gst_object_unref(pad); + gst_pad_set_active(ghost, TRUE); + gst_element_add_pad(priv->confbin, ghost); + } + + gst_element_set_state(session->tee, GST_STATE_PLAYING); + gst_element_link(session->src, priv->confbin); + + g_object_get(session->session, "sink-pad", &sinkpad, NULL); + if (session->type & PURPLE_MEDIA_SEND_AUDIO) { + gchar *name = g_strdup_printf("volume_%s", session->id); + GstElement *level; + GstElement *volume = gst_element_factory_make("volume", name); + double input_volume = purple_prefs_get_int( + "/purple/media/audio/volume/input")/10.0; + g_free(name); + name = g_strdup_printf("sendlevel_%s", session->id); + level = gst_element_factory_make("level", name); + g_free(name); + gst_bin_add(GST_BIN(priv->confbin), volume); + gst_bin_add(GST_BIN(priv->confbin), level); + gst_element_set_state(level, GST_STATE_PLAYING); + gst_element_set_state(volume, GST_STATE_PLAYING); + gst_element_link(volume, level); + gst_element_link(session->tee, volume); + srcpad = gst_element_get_static_pad(level, "src"); + g_object_set(volume, "volume", input_volume, NULL); + } else { + srcpad = gst_element_get_request_pad(session->tee, "src%d"); + } + + purple_debug_info("backend-fs2", "connecting pad: %s\n", + gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK + ? "success" : "failure"); + gst_element_set_locked_state(session->src, FALSE); + gst_object_unref(session->src); + + gst_element_set_state(session->src, GST_STATE_PLAYING); + + purple_media_manager_create_output_window(purple_media_get_manager( + priv->media), priv->media, sess_id, NULL); + + return TRUE; +} + +static gboolean +create_session(PurpleMediaBackendFs2 *self, const gchar *sess_id, + PurpleMediaSessionType type, gboolean initiator, + const gchar *transmitter) +{ + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + PurpleMediaBackendFs2Session *session; + GError *err = NULL; + GList *codec_conf = NULL, *iter = NULL; + gchar *filename = NULL; + gboolean is_nice = !strcmp(transmitter, "nice"); + + session = g_new0(PurpleMediaBackendFs2Session, 1); + + session->session = fs_conference_new_session(priv->conference, + session_type_to_fs_media_type(type), &err); + + if (err != NULL) { + purple_media_error(priv->media, + _("Error creating session: %s"), + err->message); + g_error_free(err); + g_free(session); + return FALSE; + } + + filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL); + codec_conf = fs_codec_list_from_keyfile(filename, &err); + g_free(filename); + + if (err != NULL) { + if (err->code == 4) + purple_debug_info("backend-fs2", "Couldn't read " + "fs-codec.conf: %s\n", + err->message); + else + purple_debug_error("backend-fs2", "Error reading " + "fs-codec.conf: %s\n", + err->message); + g_error_free(err); + } + + /* + * Add SPEEX if the configuration file doesn't exist or + * there isn't a speex entry. + */ + for (iter = codec_conf; iter; iter = g_list_next(iter)) { + FsCodec *codec = iter->data; + if (!g_ascii_strcasecmp(codec->encoding_name, "speex")) + break; + } + + if (iter == NULL) { + codec_conf = g_list_prepend(codec_conf, + fs_codec_new(FS_CODEC_ID_ANY, + "SPEEX", FS_MEDIA_TYPE_AUDIO, 8000)); + codec_conf = g_list_prepend(codec_conf, + fs_codec_new(FS_CODEC_ID_ANY, + "SPEEX", FS_MEDIA_TYPE_AUDIO, 16000)); + } + + fs_session_set_codec_preferences(session->session, codec_conf, NULL); + fs_codec_list_destroy(codec_conf); + + /* + * Removes a 5-7 second delay before + * receiving the src-pad-added signal. + * Only works for non-multicast FsRtpSessions. + */ + if (is_nice || !strcmp(transmitter, "rawudp")) + g_object_set(G_OBJECT(session->session), + "no-rtcp-timeout", 0, NULL); + + /* + * Hack to make x264 work with Gmail video. + */ + if (is_nice && !strcmp(sess_id, "google-video")) { + FsElementAddedNotifier *notifier = + fs_element_added_notifier_new(); + g_signal_connect(G_OBJECT(notifier), "element-added", + G_CALLBACK(gst_element_added_cb), NULL); + fs_element_added_notifier_add(notifier, + GST_BIN(priv->conference)); + } + + session->id = g_strdup(sess_id); + session->backend = self; + session->type = type; + + if (!priv->sessions) { + purple_debug_info("backend-fs2", + "Creating hash table for sessions\n"); + priv->sessions = g_hash_table_new(g_str_hash, g_str_equal); + } + + g_hash_table_insert(priv->sessions, g_strdup(session->id), session); + + if (!create_src(self, sess_id, type)) { + purple_debug_info("backend-fs2", "Error creating the src\n"); + return FALSE; + } + + return TRUE; +} + +static gboolean +create_participant(PurpleMediaBackendFs2 *self, const gchar *name) +{ + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + FsParticipant *participant; + GError *err = NULL; + + participant = fs_conference_new_participant( + priv->conference, name, &err); + + if (err) { + purple_debug_error("backend-fs2", + "Error creating participant: %s\n", + err->message); + g_error_free(err); + return FALSE; + } + + if (!priv->participants) { + purple_debug_info("backend-fs2", + "Creating hash table for participants\n"); + priv->participants = g_hash_table_new_full(g_str_hash, + g_str_equal, g_free, NULL); + } + + g_hash_table_insert(priv->participants, g_strdup(name), participant); + + return TRUE; +} + +static gboolean +src_pad_added_cb_cb(PurpleMediaBackendFs2Stream *stream) +{ + PurpleMediaBackendFs2Private *priv; + + g_return_val_if_fail(stream != NULL, FALSE); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(stream->session->backend); + stream->connected_cb_id = 0; + + purple_media_manager_create_output_window( + purple_media_get_manager(priv->media), priv->media, + stream->session->id, stream->participant); + + g_signal_emit_by_name(priv->media, "state-changed", + PURPLE_MEDIA_STATE_CONNECTED, + stream->session->id, stream->participant); + return FALSE; +} + +static void +src_pad_added_cb(FsStream *fsstream, GstPad *srcpad, + FsCodec *codec, PurpleMediaBackendFs2Stream *stream) +{ + PurpleMediaBackendFs2Private *priv; + GstPad *sinkpad; + + g_return_if_fail(FS_IS_STREAM(fsstream)); + g_return_if_fail(stream != NULL); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(stream->session->backend); + + if (stream->src == NULL) { + GstElement *sink = NULL; + + if (codec->media_type == FS_MEDIA_TYPE_AUDIO) { + GstElement *queue = NULL; + double output_volume = purple_prefs_get_int( + "/purple/media/audio/volume/output")/10.0; + /* + * Should this instead be: + * audioconvert ! audioresample ! liveadder ! + * audioresample ! audioconvert ! realsink + */ + queue = gst_element_factory_make("queue", NULL); + stream->volume = gst_element_factory_make( + "volume", NULL); + g_object_set(stream->volume, "volume", + output_volume, NULL); + stream->level = gst_element_factory_make( + "level", NULL); + stream->src = gst_element_factory_make( + "liveadder", NULL); + sink = purple_media_manager_get_element( + purple_media_get_manager(priv->media), + PURPLE_MEDIA_RECV_AUDIO, priv->media, + stream->session->id, + stream->participant); + gst_bin_add(GST_BIN(priv->confbin), queue); + gst_bin_add(GST_BIN(priv->confbin), stream->volume); + gst_bin_add(GST_BIN(priv->confbin), stream->level); + gst_bin_add(GST_BIN(priv->confbin), sink); + gst_element_set_state(sink, GST_STATE_PLAYING); + gst_element_set_state(stream->level, GST_STATE_PLAYING); + gst_element_set_state(stream->volume, GST_STATE_PLAYING); + gst_element_set_state(queue, GST_STATE_PLAYING); + gst_element_link(stream->level, sink); + gst_element_link(stream->volume, stream->level); + gst_element_link(queue, stream->volume); + sink = queue; + } else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) { + stream->src = gst_element_factory_make( + "fsfunnel", NULL); + sink = gst_element_factory_make( + "fakesink", NULL); + g_object_set(G_OBJECT(sink), "async", FALSE, NULL); + gst_bin_add(GST_BIN(priv->confbin), sink); + gst_element_set_state(sink, GST_STATE_PLAYING); + } + stream->tee = gst_element_factory_make("tee", NULL); + gst_bin_add_many(GST_BIN(priv->confbin), + stream->src, stream->tee, NULL); + gst_element_set_state(stream->tee, GST_STATE_PLAYING); + gst_element_set_state(stream->src, GST_STATE_PLAYING); + gst_element_link_many(stream->src, stream->tee, sink, NULL); + } + + sinkpad = gst_element_get_request_pad(stream->src, "sink%d"); + gst_pad_link(srcpad, sinkpad); + gst_object_unref(sinkpad); + + stream->connected_cb_id = purple_timeout_add(0, + (GSourceFunc)src_pad_added_cb_cb, stream); +} + +static gboolean +create_stream(PurpleMediaBackendFs2 *self, + const gchar *sess_id, const gchar *who, + PurpleMediaSessionType type, gboolean initiator, + const gchar *transmitter, + guint num_params, GParameter *params) +{ + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + GError *err = NULL; + FsStream *fsstream = NULL; + const gchar *stun_ip = purple_network_get_stun_ip(); + const gchar *turn_ip = purple_network_get_turn_ip(); + guint _num_params = num_params; + GParameter *_params = g_new0(GParameter, num_params + 2); + FsStreamDirection type_direction = + session_type_to_fs_stream_direction(type); + PurpleMediaBackendFs2Session *session; + PurpleMediaBackendFs2Stream *stream; + FsParticipant *participant; + + memcpy(_params, params, sizeof(GParameter) * num_params); + + if (stun_ip) { + purple_debug_info("backend-fs2", + "Setting stun-ip on new stream: %s\n", stun_ip); + + _params[_num_params].name = "stun-ip"; + g_value_init(&_params[_num_params].value, G_TYPE_STRING); + g_value_set_string(&_params[_num_params].value, stun_ip); + ++_num_params; + } + + if (turn_ip && !strcmp("nice", transmitter)) { + GValueArray *relay_info = g_value_array_new(0); + GValue value; + gint turn_port = purple_prefs_get_int( + "/purple/network/turn_port"); + const gchar *username = purple_prefs_get_string( + "/purple/network/turn_username"); + const gchar *password = purple_prefs_get_string( + "/purple/network/turn_password"); + GstStructure *turn_setup = gst_structure_new("relay-info", + "ip", G_TYPE_STRING, turn_ip, + "port", G_TYPE_UINT, turn_port, + "username", G_TYPE_STRING, username, + "password", G_TYPE_STRING, password, + NULL); + + if (!turn_setup) { + purple_debug_error("backend-fs2", + "Error creating relay info structure"); + return FALSE; + } + + memset(&value, 0, sizeof(GValue)); + g_value_init(&value, GST_TYPE_STRUCTURE); + gst_value_set_structure(&value, turn_setup); + relay_info = g_value_array_append(relay_info, &value); + gst_structure_free(turn_setup); + + purple_debug_info("backend-fs2", + "Setting relay-info on new stream\n"); + _params[_num_params].name = "relay-info"; + g_value_init(&_params[_num_params].value, + G_TYPE_VALUE_ARRAY); + g_value_set_boxed(&_params[_num_params].value, + relay_info); + g_value_array_free(relay_info); + } + + session = get_session(self, sess_id); + + if (session == NULL) { + purple_debug_error("backend-fs2", + "Couldn't find session to create stream.\n"); + return FALSE; + } + + participant = get_participant(self, who); + + if (participant == NULL) { + purple_debug_error("backend-fs2", "Couldn't find " + "participant to create stream.\n"); + return FALSE; + } + + fsstream = fs_session_new_stream(session->session, participant, + type_direction & FS_DIRECTION_RECV, transmitter, + _num_params, _params, &err); + g_free(_params); + + if (fsstream == NULL) { + if (err) { + purple_debug_error("backend-fs2", + "Error creating stream: %s\n", + err && err->message ? + err->message : "NULL"); + g_error_free(err); + } else + purple_debug_error("backend-fs2", + "Error creating stream\n"); + return FALSE; + } + + stream = g_new0(PurpleMediaBackendFs2Stream, 1); + stream->participant = g_strdup(who); + stream->session = session; + stream->stream = fsstream; + + priv->streams = g_list_append(priv->streams, stream); + + g_signal_connect(G_OBJECT(fsstream), "src-pad-added", + G_CALLBACK(src_pad_added_cb), stream); + + return TRUE; +} + +static gboolean +purple_media_backend_fs2_add_stream(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *who, + PurpleMediaSessionType type, gboolean initiator, + const gchar *transmitter, + guint num_params, GParameter *params) +{ + PurpleMediaBackendFs2 *backend = PURPLE_MEDIA_BACKEND_FS2(self); + PurpleMediaBackendFs2Private *priv = + PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(backend); + PurpleMediaBackendFs2Stream *stream; + + if (priv->conference == NULL && !init_conference(backend)) { + purple_debug_error("backend-fs2", + "Error initializing the conference.\n"); + return FALSE; + } + + if (get_session(backend, sess_id) == NULL && + !create_session(backend, sess_id, type, + initiator, transmitter)) { + purple_debug_error("backend-fs2", + "Error creating the session.\n"); + return FALSE; + } + + if (get_participant(backend, who) == NULL && + !create_participant(backend, who)) { + purple_debug_error("backend-fs2", + "Error creating the participant.\n"); + return FALSE; + } + + stream = get_stream(backend, sess_id, who); + + if (stream != NULL) { + FsStreamDirection type_direction = + session_type_to_fs_stream_direction(type); + + if (session_type_to_fs_stream_direction( + stream->session->type) != type_direction) { + /* change direction */ + g_object_set(stream->stream, "direction", + type_direction, NULL); + } + } else if (!create_stream(backend, sess_id, who, type, + initiator, transmitter, num_params, params)) { + purple_debug_error("backend-fs2", + "Error creating the stream.\n"); + return FALSE; + } + + return TRUE; +} + +static void +purple_media_backend_fs2_add_remote_candidates(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant, + GList *remote_candidates) +{ + PurpleMediaBackendFs2Private *priv; + PurpleMediaBackendFs2Stream *stream; + GError *err = NULL; + + g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self)); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + stream = get_stream(PURPLE_MEDIA_BACKEND_FS2(self), + sess_id, participant); + + if (stream == NULL) { + purple_debug_error("backend-fs2", + "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, + candidate_list_to_fs(remote_candidates)); + + if (purple_media_accepted(priv->media, sess_id, participant)) { + fs_stream_set_remote_candidates(stream->stream, + stream->remote_candidates, &err); + + if (err) { + purple_debug_error("backend-fs2", "Error adding remote" + " candidates: %s\n", err->message); + g_error_free(err); + } + } +} + +static gboolean +purple_media_backend_fs2_codecs_ready(PurpleMediaBackend *self, + const gchar *sess_id) +{ + PurpleMediaBackendFs2Private *priv; + gboolean ret; + + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + + if (sess_id != NULL) { + PurpleMediaBackendFs2Session *session = get_session( + PURPLE_MEDIA_BACKEND_FS2(self), sess_id); + + if (session == NULL) + return FALSE; + + if (session->type & (PURPLE_MEDIA_SEND_AUDIO | + PURPLE_MEDIA_SEND_VIDEO)) + g_object_get(session->session, + "codecs-ready", &ret, NULL); + else + ret = TRUE; + } else { + GList *values = g_hash_table_get_values(priv->sessions); + + for (; values; values = g_list_delete_link(values, values)) { + PurpleMediaBackendFs2Session *session = values->data; + if (session->type & (PURPLE_MEDIA_SEND_AUDIO | + PURPLE_MEDIA_SEND_VIDEO)) + g_object_get(session->session, + "codecs-ready", &ret, NULL); + else + ret = TRUE; + + if (ret == FALSE) + break; + } + + if (values != NULL) + g_list_free(values); + } + + return ret; +} + +static GList * +purple_media_backend_fs2_get_codecs(PurpleMediaBackend *self, + const gchar *sess_id) +{ + PurpleMediaBackendFs2Private *priv; + PurpleMediaBackendFs2Session *session; + GList *fscodecs; + GList *codecs; + + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + + session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id); + + if (session == NULL) + return NULL; + + g_object_get(G_OBJECT(session->session), + "codecs", &fscodecs, NULL); + codecs = codec_list_from_fs(fscodecs); + fs_codec_list_destroy(fscodecs); + + return codecs; +} + +static GList * +purple_media_backend_fs2_get_local_candidates(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant) +{ + PurpleMediaBackendFs2Stream *stream; + GList *candidates = NULL; + + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); + + stream = get_stream(PURPLE_MEDIA_BACKEND_FS2(self), + sess_id, participant); + + if (stream != NULL) + candidates = candidate_list_from_fs( + stream->local_candidates); + return candidates; +} + +static gboolean +purple_media_backend_fs2_set_remote_codecs(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant, + GList *codecs) +{ + PurpleMediaBackendFs2Stream *stream; + GList *fscodecs; + GError *err = NULL; + + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE); + stream = get_stream(PURPLE_MEDIA_BACKEND_FS2(self), + sess_id, participant); + + if (stream == NULL) + return FALSE; + + fscodecs = codec_list_to_fs(codecs); + fs_stream_set_remote_codecs(stream->stream, fscodecs, &err); + fs_codec_list_destroy(fscodecs); + + if (err) { + purple_debug_error("backend-fs2", + "Error setting remote codecs: %s\n", + err->message); + g_error_free(err); + return FALSE; + } + + return TRUE; +} + +static gboolean +purple_media_backend_fs2_set_send_codec(PurpleMediaBackend *self, + const gchar *sess_id, PurpleMediaCodec *codec) +{ + PurpleMediaBackendFs2Session *session; + FsCodec *fscodec; + GError *err = NULL; + + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE); + + session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id); + + if (session == NULL) + return FALSE; + + fscodec = codec_to_fs(codec); + fs_session_set_send_codec(session->session, fscodec, &err); + fs_codec_destroy(fscodec); + + if (err) { + purple_debug_error("media", "Error setting send codec\n"); + g_error_free(err); + return FALSE; + } + + return TRUE; +} + +GstElement * +purple_media_backend_fs2_get_src(PurpleMediaBackendFs2 *self, + const gchar *sess_id) +{ + PurpleMediaBackendFs2Session *session = get_session(self, sess_id); + return session != NULL ? session->src : NULL; +} + +GstElement * +purple_media_backend_fs2_get_tee(PurpleMediaBackendFs2 *self, + const gchar *sess_id, const gchar *who) +{ + if (sess_id != NULL && who == NULL) { + PurpleMediaBackendFs2Session *session = + get_session(self, sess_id); + return (session != NULL) ? session->tee : NULL; + } else if (sess_id != NULL && who != NULL) { + PurpleMediaBackendFs2Stream *stream = + get_stream(self, sess_id, who); + return (stream != NULL) ? stream->tee : NULL; + } + + g_return_val_if_reached(NULL); +} + +void +purple_media_backend_fs2_set_input_volume(PurpleMediaBackendFs2 *self, + const gchar *sess_id, double level) +{ + PurpleMediaBackendFs2Private *priv; + GList *sessions; + + g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self)); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + + purple_prefs_set_int("/purple/media/audio/volume/input", level); + + if (sess_id == NULL) + sessions = g_hash_table_get_values(priv->sessions); + else + sessions = g_list_append(NULL, get_session(self, sess_id)); + + for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { + PurpleMediaBackendFs2Session *session = sessions->data; + + if (session->type & PURPLE_MEDIA_SEND_AUDIO) { + gchar *name = g_strdup_printf("volume_%s", + session->id); + GstElement *volume = gst_bin_get_by_name( + GST_BIN(priv->confbin), name); + g_free(name); + g_object_set(volume, "volume", level/10.0, NULL); + } + } +} + +void +purple_media_backend_fs2_set_output_volume(PurpleMediaBackendFs2 *self, + const gchar *sess_id, const gchar *who, double level) +{ + + PurpleMediaBackendFs2Private *priv; + GList *streams; + + g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self)); + + priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); + + purple_prefs_set_int("/purple/media/audio/volume/output", level); + + streams = get_streams(self, sess_id, who); + + for (; streams; streams = g_list_delete_link(streams, streams)) { + PurpleMediaBackendFs2Stream *stream = streams->data; + + if (stream->session->type & PURPLE_MEDIA_RECV_AUDIO + && GST_IS_ELEMENT(stream->volume)) { + g_object_set(stream->volume, "volume", + level/10.0, NULL); + } + } +} diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media/backend-fs2.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/backend-fs2.h Wed Nov 11 03:07:21 2009 +0000 @@ -0,0 +1,77 @@ +/** + * @file backend-fs2.h Farsight 2 backend for 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 + */ + +/* + * This file should not yet be part of libpurple's API. + * It should remain internal only for now. + */ + +#ifndef _MEDIA_BACKEND_FS2_H_ +#define _MEDIA_BACKEND_FS2_H_ + +#include + +G_BEGIN_DECLS + +#define PURPLE_TYPE_MEDIA_BACKEND_FS2 (purple_media_backend_fs2_get_type()) +#define PURPLE_IS_MEDIA_BACKEND_FS2(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_BACKEND_FS2)) +#define PURPLE_IS_MEDIA_BACKEND_FS2_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_BACKEND_FS2)) +#define PURPLE_MEDIA_BACKEND_FS2(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2)) +#define PURPLE_MEDIA_BACKEND_FS2_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2)) +#define PURPLE_MEDIA_BACKEND_FS2_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2)) + +/** An opaque structure representing the Farsight 2 media backend. */ +typedef struct _PurpleMediaBackendFs2 PurpleMediaBackendFs2; + +/** + * Gets the type of the Farsight 2 media backend object. + * + * @return The Farsight 2 media backend's GType + * + * @since 2.7.0 + */ +GType purple_media_backend_fs2_get_type(void); + +/* + * Temporary function in order to be able to test while + * integrating with PurpleMedia + */ +#include +GstElement *purple_media_backend_fs2_get_src( + PurpleMediaBackendFs2 *self, + const gchar *sess_id); +GstElement *purple_media_backend_fs2_get_tee( + PurpleMediaBackendFs2 *self, + const gchar *sess_id, const gchar *who); +void purple_media_backend_fs2_set_input_volume(PurpleMediaBackendFs2 *self, + const gchar *sess_id, double level); +void purple_media_backend_fs2_set_output_volume(PurpleMediaBackendFs2 *self, + const gchar *sess_id, const gchar *who, double level); +/* end tmp */ + +G_END_DECLS + +#endif /* _MEDIA_BACKEND_FS2_H_ */ diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media/backend-iface.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/backend-iface.c Wed Nov 11 03:07:21 2009 +0000 @@ -0,0 +1,194 @@ +/** + * @file backend-iface.c Interface for media backend + * @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 "backend-iface.h" + +#include "marshallers.h" + +enum { + S_ERROR, + CANDIDATES_PREPARED, + CODECS_CHANGED, + NEW_CANDIDATE, + ACTIVE_CANDIDATE_PAIR, + LAST_SIGNAL +}; + +static guint purple_media_backend_signals[LAST_SIGNAL] = {0}; + +static void +purple_media_backend_base_init(gpointer iface) +{ + static gboolean is_initialized = FALSE; + + if (is_initialized) + return; + + g_object_interface_install_property(iface, + g_param_spec_string("conference-type", + "Conference Type", + "The type of conference that this backend " + "has been created to provide.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + g_object_interface_install_property(iface, + g_param_spec_object("media", + "Purple Media", + "The media object that this backend is bound to.", + PURPLE_TYPE_MEDIA, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + purple_media_backend_signals[S_ERROR] = + g_signal_new("error", G_TYPE_FROM_CLASS(iface), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + purple_media_backend_signals[CANDIDATES_PREPARED] = + g_signal_new("candidates-prepared", + G_TYPE_FROM_CLASS(iface), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + purple_smarshal_VOID__STRING_STRING, + G_TYPE_NONE, 2, G_TYPE_STRING, + G_TYPE_STRING); + purple_media_backend_signals[CODECS_CHANGED] = + g_signal_new("codecs-changed", + G_TYPE_FROM_CLASS(iface), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + g_cclosure_marshal_VOID__STRING, + G_TYPE_NONE, 1, G_TYPE_STRING); + purple_media_backend_signals[NEW_CANDIDATE] = + g_signal_new("new-candidate", + G_TYPE_FROM_CLASS(iface), + 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_backend_signals[ACTIVE_CANDIDATE_PAIR] = + g_signal_new("active-candidate-pair", + G_TYPE_FROM_CLASS(iface), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, + purple_smarshal_VOID__STRING_STRING_OBJECT_OBJECT, + G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, + PURPLE_TYPE_MEDIA_CANDIDATE, + PURPLE_TYPE_MEDIA_CANDIDATE); + + is_initialized = TRUE; +} + +GType +purple_media_backend_get_type(void) +{ + static GType iface_type = 0; + if (iface_type == 0) { + static const GTypeInfo info = { + sizeof(PurpleMediaBackendIface), + purple_media_backend_base_init, + NULL, + NULL, + NULL, + NULL, + 0, + 0, + NULL, + NULL + }; + + iface_type = g_type_register_static (G_TYPE_INTERFACE, + "PurpleMediaBackend", &info, 0); + } + + return iface_type; +} + +gboolean +purple_media_backend_add_stream(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *who, + PurpleMediaSessionType type, gboolean initiator, + const gchar *transmitter, + guint num_params, GParameter *params) +{ + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), FALSE); + return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->add_stream(self, + sess_id, who, type, initiator, transmitter, + num_params, params); +} + +void +purple_media_backend_add_remote_candidates(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant, + GList *remote_candidates) +{ + g_return_if_fail(PURPLE_IS_MEDIA_BACKEND(self)); + PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->add_remote_candidates(self, + sess_id, participant, remote_candidates); +} + +gboolean +purple_media_backend_codecs_ready(PurpleMediaBackend *self, + const gchar *sess_id) +{ + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), FALSE); + return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->codecs_ready(self, + sess_id); +} + +GList * +purple_media_backend_get_codecs(PurpleMediaBackend *self, + const gchar *sess_id) +{ + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), NULL); + return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->get_codecs(self, + sess_id); +} + +GList * +purple_media_backend_get_local_candidates(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant) +{ + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), NULL); + return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)-> + get_local_candidates(self, + sess_id, participant); +} + +gboolean +purple_media_backend_set_remote_codecs(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant, + GList *codecs) +{ + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), FALSE); + return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->set_remote_codecs( + self, sess_id, participant, codecs); +} + +gboolean +purple_media_backend_set_send_codec(PurpleMediaBackend *self, + const gchar *sess_id, PurpleMediaCodec *codec) +{ + g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND(self), FALSE); + return PURPLE_MEDIA_BACKEND_GET_INTERFACE(self)->set_send_codec(self, + sess_id, codec); +} diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media/backend-iface.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/backend-iface.h Wed Nov 11 03:07:21 2009 +0000 @@ -0,0 +1,94 @@ +/** + * @file backend-iface.h Interface for media backends + * @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 + */ + +#ifndef _MEDIA_BACKEND_IFACE_H_ +#define _MEDIA_BACKEND_IFACE_H_ + +#include "codec.h" +#include "enum-types.h" + +#include + +G_BEGIN_DECLS + +#define PURPLE_TYPE_MEDIA_BACKEND (purple_media_backend_get_type()) +#define PURPLE_IS_MEDIA_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_BACKEND)) +#define PURPLE_MEDIA_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_BACKEND, PurpleMediaBackend)) +#define PURPLE_MEDIA_BACKEND_GET_INTERFACE(inst)(G_TYPE_INSTANCE_GET_INTERFACE((inst), PURPLE_TYPE_MEDIA_BACKEND, PurpleMediaBackendIface)) + +typedef struct _PurpleMediaBackend PurpleMediaBackend; +typedef struct _PurpleMediaBackendIface PurpleMediaBackendIface; + +struct _PurpleMediaBackendIface +{ + GTypeInterface parent_iface; + + gboolean (*add_stream) (PurpleMediaBackend *self, + const gchar *sess_id, const gchar *who, + PurpleMediaSessionType type, gboolean initiator, + const gchar *transmitter, + guint num_params, GParameter *params); + void (*add_remote_candidates) (PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant, + GList *remote_candidates); + gboolean (*codecs_ready) (PurpleMediaBackend *self, + const gchar *sess_id); + GList *(*get_codecs) (PurpleMediaBackend *self, + const gchar *sess_id); + GList *(*get_local_candidates) (PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant); + gboolean (*set_remote_codecs) (PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant, + GList *codecs); + gboolean (*set_send_codec) (PurpleMediaBackend *self, + const gchar *sess_id, PurpleMediaCodec *codec); +}; + +GType purple_media_backend_get_type(void); + +gboolean purple_media_backend_add_stream(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *who, + PurpleMediaSessionType type, gboolean initiator, + const gchar *transmitter, + guint num_params, GParameter *params); +void purple_media_backend_add_remote_candidates(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant, + GList *remote_candidates); +gboolean purple_media_backend_codecs_ready(PurpleMediaBackend *self, + const gchar *sess_id); +GList *purple_media_backend_get_codecs(PurpleMediaBackend *self, + const gchar *sess_id); +GList *purple_media_backend_get_local_candidates(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant); +gboolean purple_media_backend_set_remote_codecs(PurpleMediaBackend *self, + const gchar *sess_id, const gchar *participant, + GList *codecs); +gboolean purple_media_backend_set_send_codec(PurpleMediaBackend *self, + const gchar *sess_id, PurpleMediaCodec *codec); + +G_END_DECLS + +#endif /* _MEDIA_BACKEND_IFACE_H_ */ diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media/candidate.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/candidate.c Wed Nov 11 03:07:21 2009 +0000 @@ -0,0 +1,495 @@ +/** + * @file candidate.c Candidate for 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 "candidate.h" + +/** @copydoc _PurpleMediaCandidateClass */ +typedef struct _PurpleMediaCandidateClass PurpleMediaCandidateClass; +/** @copydoc _PurpleMediaCandidatePrivate */ +typedef struct _PurpleMediaCandidatePrivate PurpleMediaCandidatePrivate; + +#define PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), \ + PURPLE_TYPE_MEDIA_CANDIDATE, \ + PurpleMediaCandidatePrivate)) + + +struct _PurpleMediaCandidateClass +{ + GObjectClass parent_class; +}; + +struct _PurpleMediaCandidate +{ + GObject parent; +}; + +G_DEFINE_TYPE(PurpleMediaCandidate, purple_media_candidate, G_TYPE_OBJECT); + +struct _PurpleMediaCandidatePrivate +{ + gchar *foundation; + guint component_id; + gchar *ip; + guint16 port; + gchar *base_ip; + guint16 base_port; + PurpleMediaNetworkProtocol proto; + guint32 priority; + PurpleMediaCandidateType type; + gchar *username; + gchar *password; + guint ttl; +}; + +enum { + PROP_CANDIDATE_0, + PROP_FOUNDATION, + PROP_COMPONENT_ID, + PROP_IP, + PROP_PORT, + PROP_BASE_IP, + PROP_BASE_PORT, + PROP_PROTOCOL, + PROP_PRIORITY, + PROP_TYPE, + PROP_USERNAME, + PROP_PASSWORD, + PROP_TTL, +}; + +static void +purple_media_candidate_init(PurpleMediaCandidate *info) +{ + PurpleMediaCandidatePrivate *priv = + PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info); + priv->foundation = NULL; + priv->component_id = 0; + priv->ip = NULL; + priv->port = 0; + priv->base_ip = NULL; + priv->proto = PURPLE_MEDIA_NETWORK_PROTOCOL_UDP; + priv->priority = 0; + priv->type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST; + priv->username = NULL; + priv->password = NULL; + priv->ttl = 0; +} + +static void +purple_media_candidate_finalize(GObject *info) +{ + PurpleMediaCandidatePrivate *priv = + PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info); + + g_free(priv->foundation); + g_free(priv->ip); + g_free(priv->base_ip); + g_free(priv->username); + g_free(priv->password); +} + +static void +purple_media_candidate_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + PurpleMediaCandidatePrivate *priv; + g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object)); + + priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_FOUNDATION: + g_free(priv->foundation); + priv->foundation = g_value_dup_string(value); + break; + case PROP_COMPONENT_ID: + priv->component_id = g_value_get_uint(value); + break; + case PROP_IP: + g_free(priv->ip); + priv->ip = g_value_dup_string(value); + break; + case PROP_PORT: + priv->port = g_value_get_uint(value); + break; + case PROP_BASE_IP: + g_free(priv->base_ip); + priv->base_ip = g_value_dup_string(value); + break; + case PROP_BASE_PORT: + priv->base_port = g_value_get_uint(value); + break; + case PROP_PROTOCOL: + priv->proto = g_value_get_enum(value); + break; + case PROP_PRIORITY: + priv->priority = g_value_get_uint(value); + break; + case PROP_TYPE: + priv->type = g_value_get_enum(value); + break; + case PROP_USERNAME: + g_free(priv->username); + priv->username = g_value_dup_string(value); + break; + case PROP_PASSWORD: + g_free(priv->password); + priv->password = g_value_dup_string(value); + break; + case PROP_TTL: + priv->ttl = g_value_get_uint(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( + object, prop_id, pspec); + break; + } +} + +static void +purple_media_candidate_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + PurpleMediaCandidatePrivate *priv; + g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object)); + + priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_FOUNDATION: + g_value_set_string(value, priv->foundation); + break; + case PROP_COMPONENT_ID: + g_value_set_uint(value, priv->component_id); + break; + case PROP_IP: + g_value_set_string(value, priv->ip); + break; + case PROP_PORT: + g_value_set_uint(value, priv->port); + break; + case PROP_BASE_IP: + g_value_set_string(value, priv->base_ip); + break; + case PROP_BASE_PORT: + g_value_set_uint(value, priv->base_port); + break; + case PROP_PROTOCOL: + g_value_set_enum(value, priv->proto); + break; + case PROP_PRIORITY: + g_value_set_uint(value, priv->priority); + break; + case PROP_TYPE: + g_value_set_enum(value, priv->type); + break; + case PROP_USERNAME: + g_value_set_string(value, priv->username); + break; + case PROP_PASSWORD: + g_value_set_string(value, priv->password); + break; + case PROP_TTL: + g_value_set_uint(value, priv->ttl); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( + object, prop_id, pspec); + break; + } +} + +static void +purple_media_candidate_class_init(PurpleMediaCandidateClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + + gobject_class->finalize = purple_media_candidate_finalize; + gobject_class->set_property = purple_media_candidate_set_property; + gobject_class->get_property = purple_media_candidate_get_property; + + g_object_class_install_property(gobject_class, PROP_FOUNDATION, + g_param_spec_string("foundation", + "Foundation", + "The foundation of the candidate.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_COMPONENT_ID, + g_param_spec_uint("component-id", + "Component ID", + "The component id of the candidate.", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_IP, + g_param_spec_string("ip", + "IP Address", + "The IP address of the candidate.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_PORT, + g_param_spec_uint("port", + "Port", + "The port of the candidate.", + 0, G_MAXUINT16, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_BASE_IP, + g_param_spec_string("base-ip", + "Base IP", + "The internal IP address of the candidate.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_BASE_PORT, + g_param_spec_uint("base-port", + "Base Port", + "The internal port of the candidate.", + 0, G_MAXUINT16, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_PROTOCOL, + g_param_spec_enum("protocol", + "Protocol", + "The protocol of the candidate.", + PURPLE_TYPE_MEDIA_NETWORK_PROTOCOL, + PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_PRIORITY, + g_param_spec_uint("priority", + "Priority", + "The priority of the candidate.", + 0, G_MAXUINT32, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_TYPE, + g_param_spec_enum("type", + "Type", + "The type of the candidate.", + PURPLE_TYPE_MEDIA_CANDIDATE_TYPE, + PURPLE_MEDIA_CANDIDATE_TYPE_HOST, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_USERNAME, + g_param_spec_string("username", + "Username", + "The username used to connect to the candidate.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_PASSWORD, + g_param_spec_string("password", + "Password", + "The password use to connect to the candidate.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_TTL, + g_param_spec_uint("ttl", + "TTL", + "The TTL of the candidate.", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + + g_type_class_add_private(klass, sizeof(PurpleMediaCandidatePrivate)); +} + +PurpleMediaCandidate * +purple_media_candidate_new(const gchar *foundation, guint component_id, + PurpleMediaCandidateType type, + PurpleMediaNetworkProtocol proto, + const gchar *ip, guint port) +{ + return g_object_new(PURPLE_TYPE_MEDIA_CANDIDATE, + "foundation", foundation, + "component-id", component_id, + "type", type, + "protocol", proto, + "ip", ip, + "port", port, NULL); +} + +PurpleMediaCandidate * +purple_media_candidate_copy(PurpleMediaCandidate *candidate) +{ + PurpleMediaCandidatePrivate *priv; + PurpleMediaCandidate *new_candidate; + + if (candidate == NULL) + return NULL; + + priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(candidate); + + new_candidate = purple_media_candidate_new(priv->foundation, + priv->component_id, priv->type, priv->proto, + priv->ip, priv->port); + g_object_set(new_candidate, + "base-ip", priv->base_ip, + "base-port", priv->base_port, + "priority", priv->priority, + "username", priv->username, + "password", priv->password, + "ttl", priv->ttl, NULL); + return new_candidate; +} + +GList * +purple_media_candidate_list_copy(GList *candidates) +{ + GList *new_list = NULL; + + for (; candidates; candidates = g_list_next(candidates)) { + new_list = g_list_prepend(new_list, + purple_media_candidate_copy(candidates->data)); + } + + new_list = g_list_reverse(new_list); + return new_list; +} + +void +purple_media_candidate_list_free(GList *candidates) +{ + for (; candidates; candidates = + g_list_delete_link(candidates, candidates)) { + g_object_unref(candidates->data); + } +} + +gchar * +purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate) +{ + gchar *foundation; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); + g_object_get(candidate, "foundation", &foundation, NULL); + return foundation; +} + +guint +purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate) +{ + guint component_id; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); + g_object_get(candidate, "component-id", &component_id, NULL); + return component_id; +} + +gchar * +purple_media_candidate_get_ip(PurpleMediaCandidate *candidate) +{ + gchar *ip; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); + g_object_get(candidate, "ip", &ip, NULL); + return ip; +} + +guint16 +purple_media_candidate_get_port(PurpleMediaCandidate *candidate) +{ + guint port; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); + g_object_get(candidate, "port", &port, NULL); + return port; +} + +gchar * +purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate) +{ + gchar *base_ip; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); + g_object_get(candidate, "base-ip", &base_ip, NULL); + return base_ip; +} + +guint16 +purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate) +{ + guint base_port; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); + g_object_get(candidate, "base_port", &base_port, NULL); + return base_port; +} + +PurpleMediaNetworkProtocol +purple_media_candidate_get_protocol(PurpleMediaCandidate *candidate) +{ + PurpleMediaNetworkProtocol protocol; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), + PURPLE_MEDIA_NETWORK_PROTOCOL_UDP); + g_object_get(candidate, "protocol", &protocol, NULL); + return protocol; +} + +guint32 +purple_media_candidate_get_priority(PurpleMediaCandidate *candidate) +{ + guint priority; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); + g_object_get(candidate, "priority", &priority, NULL); + return priority; +} + +PurpleMediaCandidateType +purple_media_candidate_get_candidate_type(PurpleMediaCandidate *candidate) +{ + PurpleMediaCandidateType type; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), + PURPLE_MEDIA_CANDIDATE_TYPE_HOST); + g_object_get(candidate, "type", &type, NULL); + return type; +} + +gchar * +purple_media_candidate_get_username(PurpleMediaCandidate *candidate) +{ + gchar *username; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); + g_object_get(candidate, "username", &username, NULL); + return username; +} + +gchar * +purple_media_candidate_get_password(PurpleMediaCandidate *candidate) +{ + gchar *password; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); + g_object_get(candidate, "password", &password, NULL); + return password; +} + +guint +purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate) +{ + guint ttl; + g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); + g_object_get(candidate, "ttl", &ttl, NULL); + return ttl; +} + diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media/candidate.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/candidate.h Wed Nov 11 03:07:21 2009 +0000 @@ -0,0 +1,125 @@ +/** + * @file candidate.h Candidate for 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 + */ + +#ifndef _PURPLE_MEDIA_CANDIDATE_H_ +#define _PURPLE_MEDIA_CANDIDATE_H_ + +#include "enum-types.h" + +#include + +G_BEGIN_DECLS + +#define PURPLE_TYPE_MEDIA_CANDIDATE (purple_media_candidate_get_type()) +#define PURPLE_IS_MEDIA_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_CANDIDATE)) +#define PURPLE_IS_MEDIA_CANDIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_CANDIDATE)) +#define PURPLE_MEDIA_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate)) +#define PURPLE_MEDIA_CANDIDATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate)) +#define PURPLE_MEDIA_CANDIDATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidate)) + +/** An opaque structure representing a network candidate (IP Address and port pair). */ +typedef struct _PurpleMediaCandidate PurpleMediaCandidate; + +/** + * Gets the type of the media candidate structure. + * + * @return The media canditate's GType + * + * @since 2.6.0 + */ +GType purple_media_candidate_get_type(void); + +/** + * Creates a PurpleMediaCandidate instance. + * + * @param foundation The foundation of the candidate. + * @param component_id The component this candidate is for. + * @param type The type of candidate. + * @param proto The protocol this component is for. + * @param ip The IP address of this component. + * @param port The network port. + * + * @return The newly created PurpleMediaCandidate instance. + * + * @since 2.6.0 + */ +PurpleMediaCandidate *purple_media_candidate_new( + const gchar *foundation, guint component_id, + PurpleMediaCandidateType type, + PurpleMediaNetworkProtocol proto, + const gchar *ip, guint port); + +/** + * Copies a PurpleMediaCandidate. + * + * @param candidate The candidate to copy. + * + * @return The copy of the PurpleMediaCandidate. + * + * @since 2.7.0 + */ +PurpleMediaCandidate *purple_media_candidate_copy( + PurpleMediaCandidate *candidate); + +/** + * Copies a GList of PurpleMediaCandidate and its contents. + * + * @param candidates The list of candidates to be copied. + * + * @return The copy of the GList. + * + * @since 2.6.0 + */ +GList *purple_media_candidate_list_copy(GList *candidates); + +/** + * Frees a GList of PurpleMediaCandidate and its contents. + * + * @param candidates The list of candidates to be freed. + * + * @since 2.6.0 + */ +void purple_media_candidate_list_free(GList *candidates); + +gchar *purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate); +guint purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate); +gchar *purple_media_candidate_get_ip(PurpleMediaCandidate *candidate); +guint16 purple_media_candidate_get_port(PurpleMediaCandidate *candidate); +gchar *purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate); +guint16 purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate); +PurpleMediaNetworkProtocol purple_media_candidate_get_protocol( + PurpleMediaCandidate *candidate); +guint32 purple_media_candidate_get_priority(PurpleMediaCandidate *candidate); +PurpleMediaCandidateType purple_media_candidate_get_candidate_type( + PurpleMediaCandidate *candidate); +gchar *purple_media_candidate_get_username(PurpleMediaCandidate *candidate); +gchar *purple_media_candidate_get_password(PurpleMediaCandidate *candidate); +guint purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate); + +G_END_DECLS + +#endif /* _PURPLE_MEDIA_CANDIDATE_H_ */ + diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media/codec.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/codec.c Wed Nov 11 03:07:21 2009 +0000 @@ -0,0 +1,419 @@ +/** + * @file codec.c Codec for 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 "codec.h" + +/** @copydoc _PurpleMediaCodecClass */ +typedef struct _PurpleMediaCodecClass PurpleMediaCodecClass; +/** @copydoc _PurpleMediaCodecPrivate */ +typedef struct _PurpleMediaCodecPrivate PurpleMediaCodecPrivate; + +#define PURPLE_MEDIA_CODEC_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE((obj), \ + PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodecPrivate)) + +struct _PurpleMediaCodecClass +{ + GObjectClass parent_class; +}; + +struct _PurpleMediaCodec +{ + GObject parent; +}; + +G_DEFINE_TYPE(PurpleMediaCodec, purple_media_codec, G_TYPE_OBJECT); + +struct _PurpleMediaCodecPrivate +{ + gint id; + char *encoding_name; + PurpleMediaSessionType media_type; + guint clock_rate; + guint channels; + GList *optional_params; +}; + +enum { + PROP_CODEC_0, + PROP_ID, + PROP_ENCODING_NAME, + PROP_MEDIA_TYPE, + PROP_CLOCK_RATE, + PROP_CHANNELS, + PROP_OPTIONAL_PARAMS, +}; + +static void +purple_media_codec_init(PurpleMediaCodec *info) +{ + PurpleMediaCodecPrivate *priv = + PURPLE_MEDIA_CODEC_GET_PRIVATE(info); + priv->encoding_name = NULL; + priv->optional_params = NULL; +} + +static void +purple_media_codec_finalize(GObject *info) +{ + PurpleMediaCodecPrivate *priv = + PURPLE_MEDIA_CODEC_GET_PRIVATE(info); + g_free(priv->encoding_name); + for (; priv->optional_params; priv->optional_params = + g_list_delete_link(priv->optional_params, + priv->optional_params)) { + g_free(priv->optional_params->data); + } +} + +static void +purple_media_codec_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + PurpleMediaCodecPrivate *priv; + g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object)); + + priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_ID: + priv->id = g_value_get_uint(value); + break; + case PROP_ENCODING_NAME: + g_free(priv->encoding_name); + priv->encoding_name = g_value_dup_string(value); + break; + case PROP_MEDIA_TYPE: + priv->media_type = g_value_get_flags(value); + break; + case PROP_CLOCK_RATE: + priv->clock_rate = g_value_get_uint(value); + break; + case PROP_CHANNELS: + priv->channels = g_value_get_uint(value); + break; + case PROP_OPTIONAL_PARAMS: + priv->optional_params = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( + object, prop_id, pspec); + break; + } +} + +static void +purple_media_codec_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + PurpleMediaCodecPrivate *priv; + g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object)); + + priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_ID: + g_value_set_uint(value, priv->id); + break; + case PROP_ENCODING_NAME: + g_value_set_string(value, priv->encoding_name); + break; + case PROP_MEDIA_TYPE: + g_value_set_flags(value, priv->media_type); + break; + case PROP_CLOCK_RATE: + g_value_set_uint(value, priv->clock_rate); + break; + case PROP_CHANNELS: + g_value_set_uint(value, priv->channels); + break; + case PROP_OPTIONAL_PARAMS: + g_value_set_pointer(value, priv->optional_params); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( + object, prop_id, pspec); + break; + } +} + +static void +purple_media_codec_class_init(PurpleMediaCodecClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + + gobject_class->finalize = purple_media_codec_finalize; + gobject_class->set_property = purple_media_codec_set_property; + gobject_class->get_property = purple_media_codec_get_property; + + g_object_class_install_property(gobject_class, PROP_ID, + g_param_spec_uint("id", + "ID", + "The numeric identifier of the codec.", + 0, G_MAXUINT, 0, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_ENCODING_NAME, + g_param_spec_string("encoding-name", + "Encoding Name", + "The name of the codec.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE, + g_param_spec_flags("media-type", + "Media Type", + "Whether this is an audio of video codec.", + PURPLE_TYPE_MEDIA_SESSION_TYPE, + PURPLE_MEDIA_NONE, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_CLOCK_RATE, + g_param_spec_uint("clock-rate", + "Create Callback", + "The function called to create this element.", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_CHANNELS, + g_param_spec_uint("channels", + "Channels", + "The number of channels in this codec.", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, PROP_OPTIONAL_PARAMS, + g_param_spec_pointer("optional-params", + "Optional Params", + "A list of optional parameters for the codec.", + G_PARAM_READWRITE)); + + g_type_class_add_private(klass, sizeof(PurpleMediaCodecPrivate)); +} + +PurpleMediaCodec * +purple_media_codec_new(int id, const char *encoding_name, + PurpleMediaSessionType media_type, guint clock_rate) +{ + PurpleMediaCodec *codec = + g_object_new(PURPLE_TYPE_MEDIA_CODEC, + "id", id, + "encoding_name", encoding_name, + "media_type", media_type, + "clock-rate", clock_rate, NULL); + return codec; +} + +guint +purple_media_codec_get_id(PurpleMediaCodec *codec) +{ + guint id; + g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0); + g_object_get(codec, "id", &id, NULL); + return id; +} + +gchar * +purple_media_codec_get_encoding_name(PurpleMediaCodec *codec) +{ + gchar *name; + g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL); + g_object_get(codec, "encoding-name", &name, NULL); + return name; +} + +guint +purple_media_codec_get_clock_rate(PurpleMediaCodec *codec) +{ + guint clock_rate; + g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0); + g_object_get(codec, "clock-rate", &clock_rate, NULL); + return clock_rate; +} + +guint +purple_media_codec_get_channels(PurpleMediaCodec *codec) +{ + guint channels; + g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0); + g_object_get(codec, "channels", &channels, NULL); + return channels; +} + +GList * +purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec) +{ + GList *optional_params; + g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL); + g_object_get(codec, "optional-params", &optional_params, NULL); + return optional_params; +} + +void +purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec, + const gchar *name, const gchar *value) +{ + PurpleMediaCodecPrivate *priv; + PurpleKeyValuePair *new_param; + + g_return_if_fail(codec != NULL); + g_return_if_fail(name != NULL && value != NULL); + + priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); + + new_param = g_new0(PurpleKeyValuePair, 1); + new_param->key = g_strdup(name); + new_param->value = g_strdup(value); + priv->optional_params = g_list_append( + priv->optional_params, new_param); +} + +void +purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec, + PurpleKeyValuePair *param) +{ + PurpleMediaCodecPrivate *priv; + + g_return_if_fail(codec != NULL && param != NULL); + + priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); + + g_free(param->key); + g_free(param->value); + g_free(param); + + priv->optional_params = + g_list_remove(priv->optional_params, param); +} + +PurpleKeyValuePair * +purple_media_codec_get_optional_parameter(PurpleMediaCodec *codec, + const gchar *name, const gchar *value) +{ + PurpleMediaCodecPrivate *priv; + GList *iter; + + g_return_val_if_fail(codec != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); + + for (iter = priv->optional_params; iter; iter = g_list_next(iter)) { + PurpleKeyValuePair *param = iter->data; + if (!g_ascii_strcasecmp(param->key, name) && + (value == NULL || + !g_ascii_strcasecmp(param->value, value))) + return param; + } + + return NULL; +} + +PurpleMediaCodec * +purple_media_codec_copy(PurpleMediaCodec *codec) +{ + PurpleMediaCodecPrivate *priv; + PurpleMediaCodec *new_codec; + GList *iter; + + if (codec == NULL) + return NULL; + + priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); + + new_codec = purple_media_codec_new(priv->id, priv->encoding_name, + priv->media_type, priv->clock_rate); + g_object_set(codec, "channels", priv->channels, NULL); + + for (iter = priv->optional_params; iter; iter = g_list_next(iter)) { + PurpleKeyValuePair *param = + (PurpleKeyValuePair*)iter->data; + purple_media_codec_add_optional_parameter(new_codec, + param->key, param->value); + } + + return new_codec; +} + +GList * +purple_media_codec_list_copy(GList *codecs) +{ + GList *new_list = NULL; + + for (; codecs; codecs = g_list_next(codecs)) { + new_list = g_list_prepend(new_list, + purple_media_codec_copy(codecs->data)); + } + + new_list = g_list_reverse(new_list); + return new_list; +} + +void +purple_media_codec_list_free(GList *codecs) +{ + for (; codecs; codecs = + g_list_delete_link(codecs, codecs)) { + g_object_unref(codecs->data); + } +} + +gchar * +purple_media_codec_to_string(const PurpleMediaCodec *codec) +{ + PurpleMediaCodecPrivate *priv; + GString *string = NULL; + GList *item; + gchar *charstring; + const gchar *media_type_str = NULL; + + if (codec == NULL) + return g_strdup("(NULL)"); + + priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); + + string = g_string_new(""); + + if (priv->media_type & PURPLE_MEDIA_AUDIO) + media_type_str = "audio"; + else if (priv->media_type & PURPLE_MEDIA_VIDEO) + media_type_str = "video"; + + g_string_printf(string, "%d: %s %s clock:%d channels:%d", priv->id, + media_type_str, priv->encoding_name, + priv->clock_rate, priv->channels); + + for (item = priv->optional_params; item; item = g_list_next (item)) { + PurpleKeyValuePair *param = item->data; + g_string_append_printf (string, " %s=%s", + param->key, (gchar *)param->value); + } + + charstring = string->str; + g_string_free (string, FALSE); + + return charstring; +} + diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media/codec.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/codec.h Wed Nov 11 03:07:21 2009 +0000 @@ -0,0 +1,162 @@ +/** + * @file codec.h Codec for 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 + */ + +#ifndef _PURPLE_MEDIA_CODEC_H_ +#define _PURPLE_MEDIA_CODEC_H_ + +#include "enum-types.h" + +/** An opaque structure representing an audio or video codec. */ +typedef struct _PurpleMediaCodec PurpleMediaCodec; + +#include "util.h" + +#include + +G_BEGIN_DECLS + +#define PURPLE_TYPE_MEDIA_CODEC (purple_media_codec_get_type()) +#define PURPLE_IS_MEDIA_CODEC(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), PURPLE_TYPE_MEDIA_CODEC)) +#define PURPLE_IS_MEDIA_CODEC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), PURPLE_TYPE_MEDIA_CODEC)) +#define PURPLE_MEDIA_CODEC(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec)) +#define PURPLE_MEDIA_CODEC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec)) +#define PURPLE_MEDIA_CODEC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodec)) + + +/** + * Gets the type of the media codec structure. + * + * @return The media codec's GType + * + * @since 2.6.0 + */ +GType purple_media_codec_get_type(void); + +/** + * Creates a new PurpleMediaCodec instance. + * + * @param id Codec identifier. + * @param encoding_name Name of the media type this encodes. + * @param media_type PurpleMediaSessionType of this codec. + * @param clock_rate The clock rate this codec encodes at, if applicable. + * + * @return The newly created PurpleMediaCodec. + * + * @since 2.6.0 + */ +PurpleMediaCodec *purple_media_codec_new(int id, const char *encoding_name, + PurpleMediaSessionType media_type, guint clock_rate); + +guint purple_media_codec_get_id(PurpleMediaCodec *codec); +gchar *purple_media_codec_get_encoding_name(PurpleMediaCodec *codec); +guint purple_media_codec_get_clock_rate(PurpleMediaCodec *codec); +guint purple_media_codec_get_channels(PurpleMediaCodec *codec); +GList *purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec); + +/** + * Adds an optional parameter to the codec. + * + * @param codec The codec to add the parameter to. + * @param name The name of the parameter to add. + * @param value The value of the parameter to add. + * + * @since 2.6.0 + */ +void purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec, + const gchar *name, const gchar *value); + +/** + * Removes an optional parameter from the codec. + * + * @param codec The codec to remove the parameter from. + * @param param A pointer to the parameter to remove. + * + * @since 2.6.0 + */ +void purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec, + PurpleKeyValuePair *param); + +/** + * Gets an optional parameter based on the values given. + * + * @param codec The codec to find the parameter in. + * @param name The name of the parameter to search for. + * @param value The value to search for or NULL. + * + * @return The value found or NULL. + * + * @since 2.6.0 + */ +PurpleKeyValuePair *purple_media_codec_get_optional_parameter( + PurpleMediaCodec *codec, const gchar *name, + const gchar *value); + +/** + * Copies a PurpleMediaCodec object. + * + * @param codec The codec to copy. + * + * @return The copy of the codec. + * + * @since 2.7.0 + */ +PurpleMediaCodec *purple_media_codec_copy(PurpleMediaCodec *codec); + +/** + * Copies a GList of PurpleMediaCodec and its contents. + * + * @param codecs The list of codecs to be copied. + * + * @return The copy of the GList. + * + * @since 2.6.0 + */ +GList *purple_media_codec_list_copy(GList *codecs); + +/** + * Frees a GList of PurpleMediaCodec and its contents. + * + * @param codecs The list of codecs to be freed. + * + * @since 2.6.0 + */ +void purple_media_codec_list_free(GList *codecs); + +/** + * Creates a string representation of the codec. + * + * @param codec The codec to create the string of. + * + * @return The new string representation. + * + * @since 2.6.0 + */ +gchar *purple_media_codec_to_string(const PurpleMediaCodec *codec); + +G_END_DECLS + +#endif /* _PURPLE_MEDIA_CODEC_H_ */ + diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media/enum-types.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/enum-types.c Wed Nov 11 03:07:21 2009 +0000 @@ -0,0 +1,213 @@ +/** + * @file enum-types.c Enum types for 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 "enum-types.h" + +/* + * PurpleMediaCandidateType + */ + +GType +purple_media_candidate_type_get_type() +{ + static GType type = 0; + if (type == 0) { + static const GEnumValue values[] = { + { PURPLE_MEDIA_CANDIDATE_TYPE_HOST, + "PURPLE_MEDIA_CANDIDATE_TYPE_HOST", + "host" }, + { PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX, + "PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX", + "srflx" }, + { PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX, + "PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX", + "prflx" }, + { PURPLE_MEDIA_CANDIDATE_TYPE_RELAY, + "PURPLE_MEDIA_CANDIDATE_TYPE_RELAY", + "relay" }, + { PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST, + "PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST", + "multicast" }, + { 0, NULL, NULL } + }; + type = g_enum_register_static("PurpleMediaCandidateType", + values); + } + return type; +} + +/* + * PurpleMediaCaps + */ + +GType +purple_media_caps_get_type() +{ + static GType type = 0; + if (type == 0) { + static const GEnumValue values[] = { + { PURPLE_MEDIA_CAPS_NONE, + "PURPLE_MEDIA_CAPS_NONE", "none" }, + { PURPLE_MEDIA_CAPS_AUDIO, + "PURPLE_MEDIA_CAPS_AUDIO", "audio" }, + { PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION, + "PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION", + "audio-single-direction" }, + { PURPLE_MEDIA_CAPS_VIDEO, + "PURPLE_MEDIA_CAPS_VIDEO", "video" }, + { PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION, + "PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION", + "video-single-direction" }, + { PURPLE_MEDIA_CAPS_AUDIO_VIDEO, + "PURPLE_MEDIA_CAPS_AUDIO_VIDEO", + "audio-video" }, + { PURPLE_MEDIA_CAPS_MODIFY_SESSION, + "PURPLE_MEDIA_CAPS_MODIFY_SESSION", + "modify-session" }, + { PURPLE_MEDIA_CAPS_CHANGE_DIRECTION, + "PURPLE_MEDIA_CAPS_CHANGE_DIRECTION", + "change-direction" }, + { 0, NULL, NULL } + }; + type = g_enum_register_static("PurpleMediaCaps", values); + } + return type; +} + +/* + * PurpleMediaInfoType + */ + +GType +purple_media_info_type_get_type() +{ + static GType type = 0; + if (type == 0) { + static const GEnumValue values[] = { + { PURPLE_MEDIA_INFO_HANGUP, + "PURPLE_MEDIA_INFO_HANGUP", "hangup" }, + { PURPLE_MEDIA_INFO_ACCEPT, + "PURPLE_MEDIA_INFO_ACCEPT", "accept" }, + { PURPLE_MEDIA_INFO_REJECT, + "PURPLE_MEDIA_INFO_REJECT", "reject" }, + { PURPLE_MEDIA_INFO_MUTE, + "PURPLE_MEDIA_INFO_MUTE", "mute" }, + { PURPLE_MEDIA_INFO_UNMUTE, + "PURPLE_MEDIA_INFO_UNMUTE", "unmute" }, + { PURPLE_MEDIA_INFO_PAUSE, + "PURPLE_MEDIA_INFO_PAUSE", "pause" }, + { PURPLE_MEDIA_INFO_UNPAUSE, + "PURPLE_MEDIA_INFO_UNPAUSE", "unpause" }, + { PURPLE_MEDIA_INFO_HOLD, + "PURPLE_MEDIA_INFO_HOLD", "hold" }, + { PURPLE_MEDIA_INFO_UNHOLD, + "PURPLE_MEDIA_INFO_HOLD", "unhold" }, + { 0, NULL, NULL } + }; + type = g_enum_register_static("PurpleMediaInfoType", values); + } + return type; +} + +/* + * PurpleMediaNetworkProtocol + */ + +GType +purple_media_network_protocol_get_type() +{ + static GType type = 0; + if (type == 0) { + static const GEnumValue values[] = { + { PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, + "PURPLE_MEDIA_NETWORK_PROTOCOL_UDP", + "udp" }, + { PURPLE_MEDIA_NETWORK_PROTOCOL_TCP, + "PURPLE_MEDIA_NETWORK_PROTOCOL_TCP", + "tcp" }, + { 0, NULL, NULL } + }; + type = g_enum_register_static("PurpleMediaNetworkProtocol", + values); + } + return type; +} + +/* + * PurpleMediaSessionType + */ + +GType +purple_media_session_type_get_type() +{ + static GType type = 0; + if (type == 0) { + static const GFlagsValue values[] = { + { PURPLE_MEDIA_NONE, + "PURPLE_MEDIA_NONE", "none" }, + { PURPLE_MEDIA_RECV_AUDIO, + "PURPLE_MEDIA_RECV_AUDIO", "recv-audio" }, + { PURPLE_MEDIA_SEND_AUDIO, + "PURPLE_MEDIA_SEND_AUDIO", "send-audio" }, + { PURPLE_MEDIA_RECV_VIDEO, + "PURPLE_MEDIA_RECV_VIDEO", "recv-video" }, + { PURPLE_MEDIA_SEND_VIDEO, + "PURPLE_MEDIA_SEND_VIDEO", "send-audio" }, + { PURPLE_MEDIA_AUDIO, + "PURPLE_MEDIA_AUDIO", "audio" }, + { PURPLE_MEDIA_VIDEO, + "PURPLE_MEDIA_VIDEO", "video" }, + { 0, NULL, NULL } + }; + type = g_flags_register_static( + "PurpleMediaSessionType", values); + } + return type; +} + +/* + * PurpleMediaState + */ + +GType +purple_media_state_changed_get_type() +{ + static GType type = 0; + if (type == 0) { + static const GEnumValue values[] = { + { PURPLE_MEDIA_STATE_NEW, + "PURPLE_MEDIA_STATE_NEW", "new" }, + { PURPLE_MEDIA_STATE_CONNECTED, + "PURPLE_MEDIA_STATE_CONNECTED", "connected" }, + { PURPLE_MEDIA_STATE_END, + "PURPLE_MEDIA_STATE_END", "end" }, + { 0, NULL, NULL } + }; + type = g_enum_register_static("PurpleMediaState", values); + } + return type; +} + diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media/enum-types.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/enum-types.h Wed Nov 11 03:07:21 2009 +0000 @@ -0,0 +1,162 @@ +/** + * @file enum-types.h Enum types for 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 + */ + +#ifndef _PURPLE_MEDIA_ENUM_TYPES_H_ +#define _PURPLE_MEDIA_ENUM_TYPES_H_ + +#include + +G_BEGIN_DECLS + +#define PURPLE_TYPE_MEDIA_CANDIDATE_TYPE (purple_media_candidate_type_get_type()) +#define PURPLE_MEDIA_TYPE_CAPS (purple_media_caps_get_type()) +#define PURPLE_MEDIA_TYPE_INFO_TYPE (purple_media_info_type_get_type()) +#define PURPLE_TYPE_MEDIA_NETWORK_PROTOCOL (purple_media_network_protocol_get_type()) +#define PURPLE_TYPE_MEDIA_SESSION_TYPE (purple_media_session_type_get_type()) +#define PURPLE_MEDIA_TYPE_STATE (purple_media_state_changed_get_type()) + +/** Media candidate types */ +typedef enum { + PURPLE_MEDIA_CANDIDATE_TYPE_HOST, + PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX, + PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX, + PURPLE_MEDIA_CANDIDATE_TYPE_RELAY, + PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST, +} PurpleMediaCandidateType; + +/** Media caps */ +typedef enum { + PURPLE_MEDIA_CAPS_NONE = 0, + PURPLE_MEDIA_CAPS_AUDIO = 1, + PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION = 1 << 1, + PURPLE_MEDIA_CAPS_VIDEO = 1 << 2, + PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION = 1 << 3, + PURPLE_MEDIA_CAPS_AUDIO_VIDEO = 1 << 4, + PURPLE_MEDIA_CAPS_MODIFY_SESSION = 1 << 5, + PURPLE_MEDIA_CAPS_CHANGE_DIRECTION = 1 << 6, +} PurpleMediaCaps; + +/** Media component types */ +typedef enum { + PURPLE_MEDIA_COMPONENT_NONE = 0, + PURPLE_MEDIA_COMPONENT_RTP = 1, + PURPLE_MEDIA_COMPONENT_RTCP = 2, +} PurpleMediaComponentType; + +/** Media info types */ +typedef enum { + PURPLE_MEDIA_INFO_HANGUP = 0, + PURPLE_MEDIA_INFO_ACCEPT, + PURPLE_MEDIA_INFO_REJECT, + PURPLE_MEDIA_INFO_MUTE, + PURPLE_MEDIA_INFO_UNMUTE, + PURPLE_MEDIA_INFO_PAUSE, + PURPLE_MEDIA_INFO_UNPAUSE, + PURPLE_MEDIA_INFO_HOLD, + PURPLE_MEDIA_INFO_UNHOLD, +} PurpleMediaInfoType; + +/** Media network protocols */ +typedef enum { + PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, + PURPLE_MEDIA_NETWORK_PROTOCOL_TCP, +} PurpleMediaNetworkProtocol; + +/** Media session types */ +typedef enum { + PURPLE_MEDIA_NONE = 0, + PURPLE_MEDIA_RECV_AUDIO = 1 << 0, + PURPLE_MEDIA_SEND_AUDIO = 1 << 1, + PURPLE_MEDIA_RECV_VIDEO = 1 << 2, + PURPLE_MEDIA_SEND_VIDEO = 1 << 3, + PURPLE_MEDIA_AUDIO = PURPLE_MEDIA_RECV_AUDIO | PURPLE_MEDIA_SEND_AUDIO, + PURPLE_MEDIA_VIDEO = PURPLE_MEDIA_RECV_VIDEO | PURPLE_MEDIA_SEND_VIDEO +} PurpleMediaSessionType; + +/** Media state-changed types */ +typedef enum { + PURPLE_MEDIA_STATE_NEW = 0, + PURPLE_MEDIA_STATE_CONNECTED, + PURPLE_MEDIA_STATE_END, +} PurpleMediaState; + +/** + * Gets the media candidate type's GType + * + * @return The media candidate type's GType. + * + * @since 2.6.0 + */ +GType purple_media_candidate_type_get_type(void); + +/** + * Gets the type of the media caps flags + * + * @return The media caps flags' GType + * + * @since 2.7.0 + */ +GType purple_media_caps_get_type(void); + +/** + * Gets the type of the info type enum + * + * @return The info type enum's GType + * + * @since 2.6.0 + */ +GType purple_media_info_type_get_type(void); + +/** + * Gets the media network protocol's GType + * + * @return The media network protocol's GType. + * + * @since 2.6.0 + */ +GType purple_media_network_protocol_get_type(void); + +/** + * Gets the media session type's GType + * + * @return The media session type's GType. + * + * @since 2.6.0 + */ +GType purple_media_session_type_get_type(void); + +/** + * Gets the type of the state-changed enum + * + * @return The state-changed enum's GType + * + * @since 2.6.0 + */ +GType purple_media_state_changed_get_type(void); + +G_END_DECLS + +#endif /* _PURPLE_MEDIA_ENUM_TYPES_ */ diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/media/media.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/media.c Wed Nov 11 03:07:21 2009 +0000 @@ -0,0 +1,1408 @@ +/** + * @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-fs2.h" +#include "media/backend-iface.h" +#include "mediamanager.h" + +#include "debug.h" + +#ifdef USE_GSTREAMER +#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 f93ac891ff01 -r e4884dbf0c02 libpurple/mediamanager.c --- a/libpurple/mediamanager.c Wed Nov 11 02:47:50 2009 +0000 +++ b/libpurple/mediamanager.c Wed Nov 11 03:07:21 2009 +0000 @@ -37,8 +37,8 @@ #endif #ifdef USE_VV +#include -#include #include #include @@ -80,6 +80,7 @@ GList *elements; GList *output_windows; gulong next_output_window_id; + GType backend_type; PurpleMediaElementInfo *video_src; PurpleMediaElementInfo *video_sink; @@ -167,6 +168,9 @@ media->priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media); media->priv->medias = NULL; media->priv->next_output_window_id = 1; +#ifdef USE_VV + media->priv->backend_type = PURPLE_TYPE_MEDIA_BACKEND_FS2; +#endif purple_prefs_add_none("/purple/media"); purple_prefs_add_none("/purple/media/audio"); @@ -314,34 +318,15 @@ { #ifdef USE_VV PurpleMedia *media; - FsConference *conference = FS_CONFERENCE(gst_element_factory_make(conference_type, NULL)); - GstStateChangeReturn ret; gboolean signal_ret; - if (conference == NULL) { - purple_conv_present_error(remote_user, account, - _("Error creating conference.")); - purple_debug_error("media", "Conference == NULL\n"); - return NULL; - } - media = PURPLE_MEDIA(g_object_new(purple_media_get_type(), "manager", manager, "account", account, - "conference", conference, + "conference-type", conference_type, "initiator", initiator, NULL)); - ret = gst_element_set_state(GST_ELEMENT(conference), GST_STATE_PLAYING); - - if (ret == GST_STATE_CHANGE_FAILURE) { - purple_conv_present_error(remote_user, account, - _("Error creating conference.")); - purple_debug_error("media", "Failed to start conference.\n"); - g_object_unref(media); - return NULL; - } - g_signal_emit(manager, purple_media_manager_signals[INIT_MEDIA], 0, media, account, remote_user, &signal_ret); @@ -930,6 +915,30 @@ #endif } +void +purple_media_manager_set_backend_type(PurpleMediaManager *manager, + GType backend_type) +{ +#ifdef USE_VV + g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager)); + + manager->priv->backend_type = backend_type; +#endif +} + +GType +purple_media_manager_get_backend_type(PurpleMediaManager *manager) +{ +#ifdef USE_VV + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), + PURPLE_MEDIA_CAPS_NONE); + + return manager->priv->backend_type; +#else + return G_TYPE_NONE; +#endif +} + #ifdef USE_GSTREAMER /* diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/mediamanager.h --- a/libpurple/mediamanager.h Wed Nov 11 02:47:50 2009 +0000 +++ b/libpurple/mediamanager.h Wed Nov 11 03:07:21 2009 +0000 @@ -213,6 +213,28 @@ */ PurpleMediaCaps purple_media_manager_get_ui_caps(PurpleMediaManager *manager); +/** + * Sets which media backend type media objects will use. + * + * @param manager The manager to set the caps on. + * @param backend_type The media backend type to use. + * + * @since 2.7.0 + */ +void purple_media_manager_set_backend_type(PurpleMediaManager *manager, + GType backend_type); + +/** + * Gets which media backend type media objects will use. + * + * @param manager The manager to get the media backend type from. + * + * @return The type of media backend type media objects will use. + * + * @since 2.7.0 + */ +GType purple_media_manager_get_backend_type(PurpleMediaManager *manager); + /*}@*/ #ifdef __cplusplus diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/protocols/jabber/jingle/rtp.c --- a/libpurple/protocols/jabber/jingle/rtp.c Wed Nov 11 02:47:50 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/rtp.c Wed Nov 11 03:07:21 2009 +0000 @@ -468,13 +468,22 @@ g_return_if_fail(JINGLE_IS_SESSION(session)); - if (type == PURPLE_MEDIA_INFO_HANGUP) { + if (type == PURPLE_MEDIA_INFO_HANGUP || + type == PURPLE_MEDIA_INFO_REJECT) { jabber_iq_send(jingle_session_terminate_packet( - session, "success")); - g_object_unref(session); - } else if (type == PURPLE_MEDIA_INFO_REJECT) { - jabber_iq_send(jingle_session_terminate_packet( - session, "decline")); + session, type == PURPLE_MEDIA_INFO_HANGUP ? + "success" : "decline")); + + g_signal_handlers_disconnect_by_func(G_OBJECT(media), + G_CALLBACK(jingle_rtp_state_changed_cb), + session); + g_signal_handlers_disconnect_by_func(G_OBJECT(media), + G_CALLBACK(jingle_rtp_stream_info_cb), + session); + g_signal_handlers_disconnect_by_func(G_OBJECT(media), + G_CALLBACK(jingle_rtp_new_candidate_cb), + session); + g_object_unref(session); } else if (type == PURPLE_MEDIA_INFO_ACCEPT && jingle_session_is_initiator(session) == FALSE) { diff -r f93ac891ff01 -r e4884dbf0c02 libpurple/util.h --- a/libpurple/util.h Wed Nov 11 02:47:50 2009 +0000 +++ b/libpurple/util.h Wed Nov 11 03:07:21 2009 +0000 @@ -42,6 +42,7 @@ typedef struct _PurpleKeyValuePair PurpleKeyValuePair; #include "account.h" +#include "signals.h" #include "xmlnode.h" #include "notify.h" diff -r f93ac891ff01 -r e4884dbf0c02 po/POTFILES.in --- a/po/POTFILES.in Wed Nov 11 02:47:50 2009 +0000 +++ b/po/POTFILES.in Wed Nov 11 03:07:21 2009 +0000 @@ -50,7 +50,7 @@ libpurple/ft.c libpurple/gconf/purple.schemas.in libpurple/log.c -libpurple/media.c +libpurple/media/media.c libpurple/mediamanager.c libpurple/plugin.c libpurple/plugins/autoaccept.c