# HG changeset patch # User maiku@pidgin.im # Date 1256155552 0 # Node ID fb2c0674e828952b04b500eb85ee7b8a38e30d27 # Parent 8a50c08c289d746bf80def6a98a2aa7cacc52c60 Move media.c into libpurple/media/. diff -r 8a50c08c289d -r fb2c0674e828 libpurple/Makefile.am --- a/libpurple/Makefile.am Tue Oct 20 21:39:16 2009 +0000 +++ b/libpurple/Makefile.am Wed Oct 21 20:05:52 2009 +0000 @@ -53,7 +53,7 @@ idle.c \ imgstore.c \ log.c \ - media.c \ + media/media.c \ mediamanager.c \ mime.c \ nat-pmp.c \ diff -r 8a50c08c289d -r fb2c0674e828 libpurple/Makefile.mingw --- a/libpurple/Makefile.mingw Tue Oct 20 21:39:16 2009 +0000 +++ b/libpurple/Makefile.mingw Wed Oct 21 20:05:52 2009 +0000 @@ -48,7 +48,7 @@ idle.c \ imgstore.c \ log.c \ - media.c \ + media/media.c \ mediamanager.c \ mime.c \ nat-pmp.c \ diff -r 8a50c08c289d -r fb2c0674e828 libpurple/media.c --- a/libpurple/media.c Tue Oct 20 21:39:16 2009 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3318 +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; - - 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_HOLD", "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) { - 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) { - g_object_set(stream->stream, "direction", - purple_media_to_fs_stream_direction( - stream->session->type & ((active) ? - ~PURPLE_MEDIA_SEND_VIDEO : - PURPLE_MEDIA_VIDEO)), 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, type_direction & - FS_DIRECTION_RECV, transmitter, - new_num_params, param, &err); - g_free(param); - } else { - fsstream = fs_session_new_stream(session->session, - participant, 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->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 8a50c08c289d -r fb2c0674e828 libpurple/media/media.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/media/media.c Wed Oct 21 20:05:52 2009 +0000 @@ -0,0 +1,3318 @@ +/** + * @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; + + 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_HOLD", "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) { + 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) { + g_object_set(stream->stream, "direction", + purple_media_to_fs_stream_direction( + stream->session->type & ((active) ? + ~PURPLE_MEDIA_SEND_VIDEO : + PURPLE_MEDIA_VIDEO)), 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, type_direction & + FS_DIRECTION_RECV, transmitter, + new_num_params, param, &err); + g_free(param); + } else { + fsstream = fs_session_new_stream(session->session, + participant, 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->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 8a50c08c289d -r fb2c0674e828 po/POTFILES.in --- a/po/POTFILES.in Tue Oct 20 21:39:16 2009 +0000 +++ b/po/POTFILES.in Wed Oct 21 20:05:52 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