Mercurial > pidgin.yaz
view libpurple/media.c @ 29368:746bf7d8b34e
Combine the CurrentMedia and MsnUserPhoneInfo structs. This makes
MsnUser smaller by the size of one pointer.
Since both of these structs are used only rarely, this ends up saving
memory for most people.
author | Mark Doliner <mark@kingant.net> |
---|---|
date | Fri, 05 Feb 2010 02:19:22 +0000 |
parents | 0629b6814963 |
children | 3f84b7069366 48f70e888c04 |
line wrap: on
line source
/** * @file media.c Media API * @ingroup core */ /* purple * * Purple is the legal property of its developers, whose names are too numerous * to list here. Please refer to the COPYRIGHT file distributed with this * source distribution. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "internal.h" #include "account.h" #include "media.h" #include "mediamanager.h" #include "network.h" #include "debug.h" #ifdef USE_GSTREAMER #include "marshallers.h" #include "media-gst.h" #endif #ifdef USE_VV #include <gst/farsight/fs-conference-iface.h> #include <gst/farsight/fs-element-added-notifier.h> /** @copydoc _PurpleMediaSession */ typedef struct _PurpleMediaSession PurpleMediaSession; /** @copydoc _PurpleMediaStream */ typedef struct _PurpleMediaStream PurpleMediaStream; /** @copydoc _PurpleMediaClass */ typedef struct _PurpleMediaClass PurpleMediaClass; /** @copydoc _PurpleMediaPrivate */ typedef struct _PurpleMediaPrivate PurpleMediaPrivate; /** @copydoc _PurpleMediaCandidateClass */ typedef struct _PurpleMediaCandidateClass PurpleMediaCandidateClass; /** @copydoc _PurpleMediaCandidatePrivate */ typedef struct _PurpleMediaCandidatePrivate PurpleMediaCandidatePrivate; /** @copydoc _PurpleMediaCodecClass */ typedef struct _PurpleMediaCodecClass PurpleMediaCodecClass; /** @copydoc _PurpleMediaCodecPrivate */ typedef struct _PurpleMediaCodecPrivate PurpleMediaCodecPrivate; /** The media class */ struct _PurpleMediaClass { GObjectClass parent_class; /**< The parent class. */ }; /** The media class's private data */ struct _PurpleMedia { GObject parent; /**< The parent of this object. */ PurpleMediaPrivate *priv; /**< The private data of this object. */ }; struct _PurpleMediaSession { gchar *id; PurpleMedia *media; GstElement *src; GstElement *tee; FsSession *session; PurpleMediaSessionType type; gboolean initiator; }; struct _PurpleMediaStream { PurpleMediaSession *session; gchar *participant; FsStream *stream; GstElement *src; GstElement *tee; GstElement *volume; GstElement *level; GList *local_candidates; GList *remote_candidates; gboolean initiator; gboolean accepted; gboolean candidates_prepared; gboolean held; gboolean paused; GList *active_local_candidates; GList *active_remote_candidates; guint connected_cb_id; }; #endif struct _PurpleMediaPrivate { #ifdef USE_VV PurpleMediaManager *manager; PurpleAccount *account; FsConference *conference; gboolean initiator; gpointer prpl_data; GHashTable *sessions; /* PurpleMediaSession table */ GHashTable *participants; /* FsParticipant table */ GList *streams; /* PurpleMediaStream table */ GstElement *confbin; #else gpointer dummy; #endif }; #ifdef USE_VV #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate)) #define PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidatePrivate)) #define PURPLE_MEDIA_CODEC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodecPrivate)) static void purple_media_class_init (PurpleMediaClass *klass); static void purple_media_init (PurpleMedia *media); static void purple_media_dispose (GObject *object); static void purple_media_finalize (GObject *object); static void purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void purple_media_new_local_candidate_cb(FsStream *stream, FsCandidate *local_candidate, PurpleMediaSession *session); static void purple_media_candidates_prepared_cb(FsStream *stream, PurpleMediaSession *session); static void purple_media_candidate_pair_established_cb(FsStream *stream, FsCandidate *native_candidate, FsCandidate *remote_candidate, PurpleMediaSession *session); static gboolean media_bus_call(GstBus *bus, GstMessage *msg, PurpleMedia *media); static GObjectClass *parent_class = NULL; enum { S_ERROR, CANDIDATES_PREPARED, CODECS_CHANGED, LEVEL, NEW_CANDIDATE, STATE_CHANGED, STREAM_INFO, LAST_SIGNAL }; static guint purple_media_signals[LAST_SIGNAL] = {0}; enum { PROP_0, PROP_MANAGER, PROP_ACCOUNT, PROP_CONFERENCE, PROP_INITIATOR, PROP_PRPL_DATA, }; #endif /* * PurpleMediaElementType */ GType purple_media_session_type_get_type() { static GType type = 0; if (type == 0) { static const GFlagsValue values[] = { { PURPLE_MEDIA_NONE, "PURPLE_MEDIA_NONE", "none" }, { PURPLE_MEDIA_RECV_AUDIO, "PURPLE_MEDIA_RECV_AUDIO", "recv-audio" }, { PURPLE_MEDIA_SEND_AUDIO, "PURPLE_MEDIA_SEND_AUDIO", "send-audio" }, { PURPLE_MEDIA_RECV_VIDEO, "PURPLE_MEDIA_RECV_VIDEO", "recv-video" }, { PURPLE_MEDIA_SEND_VIDEO, "PURPLE_MEDIA_SEND_VIDEO", "send-audio" }, { PURPLE_MEDIA_AUDIO, "PURPLE_MEDIA_AUDIO", "audio" }, { PURPLE_MEDIA_VIDEO, "PURPLE_MEDIA_VIDEO", "video" }, { 0, NULL, NULL } }; type = g_flags_register_static( "PurpleMediaSessionType", values); } return type; } GType purple_media_get_type() { #ifdef USE_VV static GType type = 0; if (type == 0) { static const GTypeInfo info = { sizeof(PurpleMediaClass), NULL, NULL, (GClassInitFunc) purple_media_class_init, NULL, NULL, sizeof(PurpleMedia), 0, (GInstanceInitFunc) purple_media_init, NULL }; type = g_type_register_static(G_TYPE_OBJECT, "PurpleMedia", &info, 0); } return type; #else return G_TYPE_NONE; #endif } GType purple_media_state_changed_get_type() { static GType type = 0; if (type == 0) { static const GEnumValue values[] = { { PURPLE_MEDIA_STATE_NEW, "PURPLE_MEDIA_STATE_NEW", "new" }, { PURPLE_MEDIA_STATE_CONNECTED, "PURPLE_MEDIA_STATE_CONNECTED", "connected" }, { PURPLE_MEDIA_STATE_END, "PURPLE_MEDIA_STATE_END", "end" }, { 0, NULL, NULL } }; type = g_enum_register_static("PurpleMediaState", values); } return type; } GType purple_media_info_type_get_type() { static GType type = 0; if (type == 0) { static const GEnumValue values[] = { { PURPLE_MEDIA_INFO_HANGUP, "PURPLE_MEDIA_INFO_HANGUP", "hangup" }, { PURPLE_MEDIA_INFO_ACCEPT, "PURPLE_MEDIA_INFO_ACCEPT", "accept" }, { PURPLE_MEDIA_INFO_REJECT, "PURPLE_MEDIA_INFO_REJECT", "reject" }, { PURPLE_MEDIA_INFO_MUTE, "PURPLE_MEDIA_INFO_MUTE", "mute" }, { PURPLE_MEDIA_INFO_UNMUTE, "PURPLE_MEDIA_INFO_UNMUTE", "unmute" }, { PURPLE_MEDIA_INFO_PAUSE, "PURPLE_MEDIA_INFO_PAUSE", "pause" }, { PURPLE_MEDIA_INFO_UNPAUSE, "PURPLE_MEDIA_INFO_UNPAUSE", "unpause" }, { PURPLE_MEDIA_INFO_HOLD, "PURPLE_MEDIA_INFO_HOLD", "hold" }, { PURPLE_MEDIA_INFO_UNHOLD, "PURPLE_MEDIA_INFO_UNHOLD", "unhold" }, { 0, NULL, NULL } }; type = g_enum_register_static("PurpleMediaInfoType", values); } return type; } #ifdef USE_VV static void purple_media_class_init (PurpleMediaClass *klass) { GObjectClass *gobject_class = (GObjectClass*)klass; parent_class = g_type_class_peek_parent(klass); gobject_class->dispose = purple_media_dispose; gobject_class->finalize = purple_media_finalize; gobject_class->set_property = purple_media_set_property; gobject_class->get_property = purple_media_get_property; g_object_class_install_property(gobject_class, PROP_MANAGER, g_param_spec_object("manager", "Purple Media Manager", "The media manager that contains this media session.", PURPLE_TYPE_MEDIA_MANAGER, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_ACCOUNT, g_param_spec_pointer("account", "PurpleAccount", "The account this media session is on.", G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_CONFERENCE, g_param_spec_object("conference", "Farsight conference", "The FsConference associated with this media.", FS_TYPE_CONFERENCE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE)); g_object_class_install_property(gobject_class, PROP_INITIATOR, g_param_spec_boolean("initiator", "initiator", "If the local user initiated the conference.", FALSE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_PRPL_DATA, g_param_spec_pointer("prpl-data", "gpointer", "Data the prpl plugin set on the media session.", G_PARAM_READWRITE)); purple_media_signals[S_ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, purple_smarshal_VOID__STRING_STRING, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING); purple_media_signals[CODECS_CHANGED] = g_signal_new("codecs-changed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING); purple_media_signals[LEVEL] = g_signal_new("level", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, purple_smarshal_VOID__STRING_STRING_DOUBLE, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE); purple_media_signals[NEW_CANDIDATE] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, purple_smarshal_VOID__POINTER_POINTER_OBJECT, G_TYPE_NONE, 3, G_TYPE_POINTER, G_TYPE_POINTER, PURPLE_TYPE_MEDIA_CANDIDATE); purple_media_signals[STATE_CHANGED] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, purple_smarshal_VOID__ENUM_STRING_STRING, G_TYPE_NONE, 3, PURPLE_MEDIA_TYPE_STATE, G_TYPE_STRING, G_TYPE_STRING); purple_media_signals[STREAM_INFO] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, purple_smarshal_VOID__ENUM_STRING_STRING_BOOLEAN, G_TYPE_NONE, 4, PURPLE_MEDIA_TYPE_INFO_TYPE, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN); g_type_class_add_private(klass, sizeof(PurpleMediaPrivate)); } static void purple_media_init (PurpleMedia *media) { media->priv = PURPLE_MEDIA_GET_PRIVATE(media); memset(media->priv, 0, sizeof(*media->priv)); } static void purple_media_stream_free(PurpleMediaStream *stream) { if (stream == NULL) return; /* Remove the connected_cb timeout */ if (stream->connected_cb_id != 0) purple_timeout_remove(stream->connected_cb_id); g_free(stream->participant); if (stream->local_candidates) fs_candidate_list_destroy(stream->local_candidates); if (stream->remote_candidates) fs_candidate_list_destroy(stream->remote_candidates); if (stream->active_local_candidates) fs_candidate_list_destroy(stream->active_local_candidates); if (stream->active_remote_candidates) fs_candidate_list_destroy(stream->active_remote_candidates); g_free(stream); } static void purple_media_session_free(PurpleMediaSession *session) { if (session == NULL) return; g_free(session->id); g_free(session); } static void purple_media_dispose(GObject *media) { PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media); GList *iter = NULL; purple_debug_info("media","purple_media_dispose\n"); purple_media_manager_remove_media(priv->manager, PURPLE_MEDIA(media)); if (priv->confbin) { gst_element_set_locked_state(priv->confbin, TRUE); gst_element_set_state(GST_ELEMENT(priv->confbin), GST_STATE_NULL); gst_bin_remove(GST_BIN(purple_media_manager_get_pipeline( priv->manager)), priv->confbin); priv->confbin = NULL; priv->conference = NULL; } for (iter = priv->streams; iter; iter = g_list_next(iter)) { PurpleMediaStream *stream = iter->data; if (stream->stream) { g_object_unref(stream->stream); stream->stream = NULL; } } if (priv->sessions) { GList *sessions = g_hash_table_get_values(priv->sessions); for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { PurpleMediaSession *session = sessions->data; if (session->session) { g_object_unref(session->session); session->session = NULL; } } } if (priv->participants) { GList *participants = g_hash_table_get_values(priv->participants); for (; participants; participants = g_list_delete_link(participants, participants)) g_object_unref(participants->data); } if (priv->manager) { GstElement *pipeline = purple_media_manager_get_pipeline( priv->manager); GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); g_signal_handlers_disconnect_matched(G_OBJECT(bus), G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, 0, media_bus_call, media); gst_object_unref(bus); g_object_unref(priv->manager); priv->manager = NULL; } G_OBJECT_CLASS(parent_class)->dispose(media); } static void purple_media_finalize(GObject *media) { PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media); purple_debug_info("media","purple_media_finalize\n"); for (; priv->streams; priv->streams = g_list_delete_link(priv->streams, priv->streams)) purple_media_stream_free(priv->streams->data); if (priv->sessions) { GList *sessions = g_hash_table_get_values(priv->sessions); for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { purple_media_session_free(sessions->data); } g_hash_table_destroy(priv->sessions); } G_OBJECT_CLASS(parent_class)->finalize(media); } static void purple_media_setup_pipeline(PurpleMedia *media) { GstBus *bus; gchar *name; GstElement *pipeline; if (media->priv->conference == NULL || media->priv->manager == NULL) return; pipeline = purple_media_manager_get_pipeline(media->priv->manager); name = g_strdup_printf("conf_%p", media->priv->conference); media->priv->confbin = gst_bin_new(name); g_free(name); bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); g_signal_connect(G_OBJECT(bus), "message", G_CALLBACK(media_bus_call), media); gst_object_unref(bus); gst_bin_add(GST_BIN(pipeline), media->priv->confbin); gst_bin_add(GST_BIN(media->priv->confbin), GST_ELEMENT(media->priv->conference)); gst_element_set_state(GST_ELEMENT(media->priv->confbin), GST_STATE_PLAYING); } static void purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PurpleMedia *media; g_return_if_fail(PURPLE_IS_MEDIA(object)); media = PURPLE_MEDIA(object); switch (prop_id) { case PROP_MANAGER: media->priv->manager = g_value_get_object(value); g_object_ref(media->priv->manager); purple_media_setup_pipeline(media); break; case PROP_ACCOUNT: media->priv->account = g_value_get_pointer(value); break; case PROP_CONFERENCE: { if (media->priv->conference) gst_object_unref(media->priv->conference); media->priv->conference = g_value_get_object(value); gst_object_ref(media->priv->conference); purple_media_setup_pipeline(media); break; } case PROP_INITIATOR: media->priv->initiator = g_value_get_boolean(value); break; case PROP_PRPL_DATA: media->priv->prpl_data = g_value_get_pointer(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PurpleMedia *media; g_return_if_fail(PURPLE_IS_MEDIA(object)); media = PURPLE_MEDIA(object); switch (prop_id) { case PROP_MANAGER: g_value_set_object(value, media->priv->manager); break; case PROP_ACCOUNT: g_value_set_pointer(value, media->priv->account); break; case PROP_CONFERENCE: g_value_set_object(value, media->priv->conference); break; case PROP_INITIATOR: g_value_set_boolean(value, media->priv->initiator); break; case PROP_PRPL_DATA: g_value_set_pointer(value, media->priv->prpl_data); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } #endif /* * PurpleMediaCandidateType */ GType purple_media_candidate_type_get_type() { static GType type = 0; if (type == 0) { static const GEnumValue values[] = { { PURPLE_MEDIA_CANDIDATE_TYPE_HOST, "PURPLE_MEDIA_CANDIDATE_TYPE_HOST", "host" }, { PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX, "PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX", "srflx" }, { PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX, "PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX", "prflx" }, { PURPLE_MEDIA_CANDIDATE_TYPE_RELAY, "PPURPLE_MEDIA_CANDIDATE_TYPE_RELAY", "relay" }, { PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST, "PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST", "multicast" }, { 0, NULL, NULL } }; type = g_enum_register_static("PurpleMediaCandidateType", values); } return type; } /* * PurpleMediaNetworkProtocol */ GType purple_media_network_protocol_get_type() { static GType type = 0; if (type == 0) { static const GEnumValue values[] = { { PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, "PURPLE_MEDIA_NETWORK_PROTOCOL_UDP", "udp" }, { PURPLE_MEDIA_NETWORK_PROTOCOL_TCP, "PURPLE_MEDIA_NETWORK_PROTOCOL_TCP", "tcp" }, { 0, NULL, NULL } }; type = g_enum_register_static("PurpleMediaNetworkProtocol", values); } return type; } /* * PurpleMediaCandidate */ struct _PurpleMediaCandidateClass { GObjectClass parent_class; }; struct _PurpleMediaCandidate { GObject parent; }; #ifdef USE_VV struct _PurpleMediaCandidatePrivate { gchar *foundation; guint component_id; gchar *ip; guint16 port; gchar *base_ip; guint16 base_port; PurpleMediaNetworkProtocol proto; guint32 priority; PurpleMediaCandidateType type; gchar *username; gchar *password; guint ttl; }; enum { PROP_CANDIDATE_0, PROP_FOUNDATION, PROP_COMPONENT_ID, PROP_IP, PROP_PORT, PROP_BASE_IP, PROP_BASE_PORT, PROP_PROTOCOL, PROP_PRIORITY, PROP_TYPE, PROP_USERNAME, PROP_PASSWORD, PROP_TTL, }; static void purple_media_candidate_init(PurpleMediaCandidate *info) { PurpleMediaCandidatePrivate *priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info); priv->foundation = NULL; priv->component_id = 0; priv->ip = NULL; priv->port = 0; priv->base_ip = NULL; priv->proto = PURPLE_MEDIA_NETWORK_PROTOCOL_UDP; priv->priority = 0; priv->type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST; priv->username = NULL; priv->password = NULL; priv->ttl = 0; } static void purple_media_candidate_finalize(GObject *info) { PurpleMediaCandidatePrivate *priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info); g_free(priv->foundation); g_free(priv->ip); g_free(priv->base_ip); g_free(priv->username); g_free(priv->password); } static void purple_media_candidate_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PurpleMediaCandidatePrivate *priv; g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object)); priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object); switch (prop_id) { case PROP_FOUNDATION: g_free(priv->foundation); priv->foundation = g_value_dup_string(value); break; case PROP_COMPONENT_ID: priv->component_id = g_value_get_uint(value); break; case PROP_IP: g_free(priv->ip); priv->ip = g_value_dup_string(value); break; case PROP_PORT: priv->port = g_value_get_uint(value); break; case PROP_BASE_IP: g_free(priv->base_ip); priv->base_ip = g_value_dup_string(value); break; case PROP_BASE_PORT: priv->base_port = g_value_get_uint(value); break; case PROP_PROTOCOL: priv->proto = g_value_get_enum(value); break; case PROP_PRIORITY: priv->priority = g_value_get_uint(value); break; case PROP_TYPE: priv->type = g_value_get_enum(value); break; case PROP_USERNAME: g_free(priv->username); priv->username = g_value_dup_string(value); break; case PROP_PASSWORD: g_free(priv->password); priv->password = g_value_dup_string(value); break; case PROP_TTL: priv->ttl = g_value_get_uint(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec); break; } } static void purple_media_candidate_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PurpleMediaCandidatePrivate *priv; g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object)); priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object); switch (prop_id) { case PROP_FOUNDATION: g_value_set_string(value, priv->foundation); break; case PROP_COMPONENT_ID: g_value_set_uint(value, priv->component_id); break; case PROP_IP: g_value_set_string(value, priv->ip); break; case PROP_PORT: g_value_set_uint(value, priv->port); break; case PROP_BASE_IP: g_value_set_string(value, priv->base_ip); break; case PROP_BASE_PORT: g_value_set_uint(value, priv->base_port); break; case PROP_PROTOCOL: g_value_set_enum(value, priv->proto); break; case PROP_PRIORITY: g_value_set_uint(value, priv->priority); break; case PROP_TYPE: g_value_set_enum(value, priv->type); break; case PROP_USERNAME: g_value_set_string(value, priv->username); break; case PROP_PASSWORD: g_value_set_string(value, priv->password); break; case PROP_TTL: g_value_set_uint(value, priv->ttl); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec); break; } } static void purple_media_candidate_class_init(PurpleMediaCandidateClass *klass) { GObjectClass *gobject_class = (GObjectClass*)klass; gobject_class->finalize = purple_media_candidate_finalize; gobject_class->set_property = purple_media_candidate_set_property; gobject_class->get_property = purple_media_candidate_get_property; g_object_class_install_property(gobject_class, PROP_FOUNDATION, g_param_spec_string("foundation", "Foundation", "The foundation of the candidate.", NULL, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_COMPONENT_ID, g_param_spec_uint("component-id", "Component ID", "The component id of the candidate.", 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_IP, g_param_spec_string("ip", "IP Address", "The IP address of the candidate.", NULL, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_PORT, g_param_spec_uint("port", "Port", "The port of the candidate.", 0, G_MAXUINT16, 0, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_BASE_IP, g_param_spec_string("base-ip", "Base IP", "The internal IP address of the candidate.", NULL, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_BASE_PORT, g_param_spec_uint("base-port", "Base Port", "The internal port of the candidate.", 0, G_MAXUINT16, 0, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_PROTOCOL, g_param_spec_enum("protocol", "Protocol", "The protocol of the candidate.", PURPLE_TYPE_MEDIA_NETWORK_PROTOCOL, PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_PRIORITY, g_param_spec_uint("priority", "Priority", "The priority of the candidate.", 0, G_MAXUINT32, 0, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_TYPE, g_param_spec_enum("type", "Type", "The type of the candidate.", PURPLE_TYPE_MEDIA_CANDIDATE_TYPE, PURPLE_MEDIA_CANDIDATE_TYPE_HOST, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_USERNAME, g_param_spec_string("username", "Username", "The username used to connect to the candidate.", NULL, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_PASSWORD, g_param_spec_string("password", "Password", "The password use to connect to the candidate.", NULL, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_TTL, g_param_spec_uint("ttl", "TTL", "The TTL of the candidate.", 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_type_class_add_private(klass, sizeof(PurpleMediaCandidatePrivate)); } G_DEFINE_TYPE(PurpleMediaCandidate, purple_media_candidate, G_TYPE_OBJECT); #else GType purple_media_candidate_get_type() { return G_TYPE_NONE; } #endif PurpleMediaCandidate * purple_media_candidate_new(const gchar *foundation, guint component_id, PurpleMediaCandidateType type, PurpleMediaNetworkProtocol proto, const gchar *ip, guint port) { return g_object_new(PURPLE_TYPE_MEDIA_CANDIDATE, "foundation", foundation, "component-id", component_id, "type", type, "protocol", proto, "ip", ip, "port", port, NULL); } static PurpleMediaCandidate * purple_media_candidate_copy(PurpleMediaCandidate *candidate) { #ifdef USE_VV PurpleMediaCandidatePrivate *priv; PurpleMediaCandidate *new_candidate; if (candidate == NULL) return NULL; priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(candidate); new_candidate = purple_media_candidate_new(priv->foundation, priv->component_id, priv->type, priv->proto, priv->ip, priv->port); g_object_set(new_candidate, "base-ip", priv->base_ip, "base-port", priv->base_port, "priority", priv->priority, "username", priv->username, "password", priv->password, "ttl", priv->ttl, NULL); return new_candidate; #else return NULL; #endif } #ifdef USE_VV static FsCandidate * purple_media_candidate_to_fs(PurpleMediaCandidate *candidate) { PurpleMediaCandidatePrivate *priv; FsCandidate *fscandidate; if (candidate == NULL) return NULL; priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(candidate); fscandidate = fs_candidate_new(priv->foundation, priv->component_id, priv->type, priv->proto, priv->ip, priv->port); fscandidate->base_ip = g_strdup(priv->base_ip); fscandidate->base_port = priv->base_port; fscandidate->priority = priv->priority; fscandidate->username = g_strdup(priv->username); fscandidate->password = g_strdup(priv->password); fscandidate->ttl = priv->ttl; return fscandidate; } static PurpleMediaCandidate * purple_media_candidate_from_fs(FsCandidate *fscandidate) { PurpleMediaCandidate *candidate; if (fscandidate == NULL) return NULL; candidate = purple_media_candidate_new(fscandidate->foundation, fscandidate->component_id, fscandidate->type, fscandidate->proto, fscandidate->ip, fscandidate->port); g_object_set(candidate, "base-ip", fscandidate->base_ip, "base-port", fscandidate->base_port, "priority", fscandidate->priority, "username", fscandidate->username, "password", fscandidate->password, "ttl", fscandidate->ttl, NULL); return candidate; } static GList * purple_media_candidate_list_from_fs(GList *candidates) { GList *new_list = NULL; for (; candidates; candidates = g_list_next(candidates)) { new_list = g_list_prepend(new_list, purple_media_candidate_from_fs( candidates->data)); } new_list = g_list_reverse(new_list); return new_list; } static GList * purple_media_candidate_list_to_fs(GList *candidates) { GList *new_list = NULL; for (; candidates; candidates = g_list_next(candidates)) { new_list = g_list_prepend(new_list, purple_media_candidate_to_fs( candidates->data)); } new_list = g_list_reverse(new_list); return new_list; } #endif GList * purple_media_candidate_list_copy(GList *candidates) { GList *new_list = NULL; for (; candidates; candidates = g_list_next(candidates)) { new_list = g_list_prepend(new_list, purple_media_candidate_copy(candidates->data)); } new_list = g_list_reverse(new_list); return new_list; } void purple_media_candidate_list_free(GList *candidates) { for (; candidates; candidates = g_list_delete_link(candidates, candidates)) { g_object_unref(candidates->data); } } gchar * purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate) { gchar *foundation; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); g_object_get(candidate, "foundation", &foundation, NULL); return foundation; } guint purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate) { guint component_id; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); g_object_get(candidate, "component-id", &component_id, NULL); return component_id; } gchar * purple_media_candidate_get_ip(PurpleMediaCandidate *candidate) { gchar *ip; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); g_object_get(candidate, "ip", &ip, NULL); return ip; } guint16 purple_media_candidate_get_port(PurpleMediaCandidate *candidate) { guint port; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); g_object_get(candidate, "port", &port, NULL); return port; } gchar * purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate) { gchar *base_ip; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); g_object_get(candidate, "base-ip", &base_ip, NULL); return base_ip; } guint16 purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate) { guint base_port; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); g_object_get(candidate, "base_port", &base_port, NULL); return base_port; } PurpleMediaNetworkProtocol purple_media_candidate_get_protocol(PurpleMediaCandidate *candidate) { PurpleMediaNetworkProtocol protocol; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), PURPLE_MEDIA_NETWORK_PROTOCOL_UDP); g_object_get(candidate, "protocol", &protocol, NULL); return protocol; } guint32 purple_media_candidate_get_priority(PurpleMediaCandidate *candidate) { guint priority; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); g_object_get(candidate, "priority", &priority, NULL); return priority; } PurpleMediaCandidateType purple_media_candidate_get_candidate_type(PurpleMediaCandidate *candidate) { PurpleMediaCandidateType type; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), PURPLE_MEDIA_CANDIDATE_TYPE_HOST); g_object_get(candidate, "type", &type, NULL); return type; } gchar * purple_media_candidate_get_username(PurpleMediaCandidate *candidate) { gchar *username; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); g_object_get(candidate, "username", &username, NULL); return username; } gchar * purple_media_candidate_get_password(PurpleMediaCandidate *candidate) { gchar *password; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL); g_object_get(candidate, "password", &password, NULL); return password; } guint purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate) { guint ttl; g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0); g_object_get(candidate, "ttl", &ttl, NULL); return ttl; } #ifdef USE_VV static FsMediaType purple_media_to_fs_media_type(PurpleMediaSessionType type) { if (type & PURPLE_MEDIA_AUDIO) return FS_MEDIA_TYPE_AUDIO; else if (type & PURPLE_MEDIA_VIDEO) return FS_MEDIA_TYPE_VIDEO; else return 0; } static FsStreamDirection purple_media_to_fs_stream_direction(PurpleMediaSessionType type) { if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_AUDIO || (type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO) return FS_DIRECTION_BOTH; else if ((type & PURPLE_MEDIA_SEND_AUDIO) || (type & PURPLE_MEDIA_SEND_VIDEO)) return FS_DIRECTION_SEND; else if ((type & PURPLE_MEDIA_RECV_AUDIO) || (type & PURPLE_MEDIA_RECV_VIDEO)) return FS_DIRECTION_RECV; else return FS_DIRECTION_NONE; } static PurpleMediaSessionType purple_media_from_fs(FsMediaType type, FsStreamDirection direction) { PurpleMediaSessionType result = PURPLE_MEDIA_NONE; if (type == FS_MEDIA_TYPE_AUDIO) { if (direction & FS_DIRECTION_SEND) result |= PURPLE_MEDIA_SEND_AUDIO; if (direction & FS_DIRECTION_RECV) result |= PURPLE_MEDIA_RECV_AUDIO; } else if (type == FS_MEDIA_TYPE_VIDEO) { if (direction & FS_DIRECTION_SEND) result |= PURPLE_MEDIA_SEND_VIDEO; if (direction & FS_DIRECTION_RECV) result |= PURPLE_MEDIA_RECV_VIDEO; } return result; } #endif /* * PurpleMediaCodec */ struct _PurpleMediaCodecClass { GObjectClass parent_class; }; struct _PurpleMediaCodec { GObject parent; }; #ifdef USE_VV struct _PurpleMediaCodecPrivate { gint id; char *encoding_name; PurpleMediaSessionType media_type; guint clock_rate; guint channels; GList *optional_params; }; enum { PROP_CODEC_0, PROP_ID, PROP_ENCODING_NAME, PROP_MEDIA_TYPE, PROP_CLOCK_RATE, PROP_CHANNELS, PROP_OPTIONAL_PARAMS, }; static void purple_media_codec_init(PurpleMediaCodec *info) { PurpleMediaCodecPrivate *priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(info); priv->encoding_name = NULL; priv->optional_params = NULL; } static void purple_media_codec_finalize(GObject *info) { PurpleMediaCodecPrivate *priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(info); g_free(priv->encoding_name); for (; priv->optional_params; priv->optional_params = g_list_delete_link(priv->optional_params, priv->optional_params)) { g_free(priv->optional_params->data); } } static void purple_media_codec_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { PurpleMediaCodecPrivate *priv; g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object)); priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object); switch (prop_id) { case PROP_ID: priv->id = g_value_get_uint(value); break; case PROP_ENCODING_NAME: g_free(priv->encoding_name); priv->encoding_name = g_value_dup_string(value); break; case PROP_MEDIA_TYPE: priv->media_type = g_value_get_flags(value); break; case PROP_CLOCK_RATE: priv->clock_rate = g_value_get_uint(value); break; case PROP_CHANNELS: priv->channels = g_value_get_uint(value); break; case PROP_OPTIONAL_PARAMS: priv->optional_params = g_value_get_pointer(value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec); break; } } static void purple_media_codec_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { PurpleMediaCodecPrivate *priv; g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object)); priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object); switch (prop_id) { case PROP_ID: g_value_set_uint(value, priv->id); break; case PROP_ENCODING_NAME: g_value_set_string(value, priv->encoding_name); break; case PROP_MEDIA_TYPE: g_value_set_flags(value, priv->media_type); break; case PROP_CLOCK_RATE: g_value_set_uint(value, priv->clock_rate); break; case PROP_CHANNELS: g_value_set_uint(value, priv->channels); break; case PROP_OPTIONAL_PARAMS: g_value_set_pointer(value, priv->optional_params); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID( object, prop_id, pspec); break; } } static void purple_media_codec_class_init(PurpleMediaCodecClass *klass) { GObjectClass *gobject_class = (GObjectClass*)klass; gobject_class->finalize = purple_media_codec_finalize; gobject_class->set_property = purple_media_codec_set_property; gobject_class->get_property = purple_media_codec_get_property; g_object_class_install_property(gobject_class, PROP_ID, g_param_spec_uint("id", "ID", "The numeric identifier of the codec.", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_ENCODING_NAME, g_param_spec_string("encoding-name", "Encoding Name", "The name of the codec.", NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE, g_param_spec_flags("media-type", "Media Type", "Whether this is an audio of video codec.", PURPLE_TYPE_MEDIA_SESSION_TYPE, PURPLE_MEDIA_NONE, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_CLOCK_RATE, g_param_spec_uint("clock-rate", "Create Callback", "The function called to create this element.", 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_CHANNELS, g_param_spec_uint("channels", "Channels", "The number of channels in this codec.", 0, G_MAXUINT, 0, G_PARAM_READWRITE)); g_object_class_install_property(gobject_class, PROP_OPTIONAL_PARAMS, g_param_spec_pointer("optional-params", "Optional Params", "A list of optional parameters for the codec.", G_PARAM_READWRITE)); g_type_class_add_private(klass, sizeof(PurpleMediaCodecPrivate)); } G_DEFINE_TYPE(PurpleMediaCodec, purple_media_codec, G_TYPE_OBJECT); #else GType purple_media_codec_get_type() { return G_TYPE_NONE; } #endif guint purple_media_codec_get_id(PurpleMediaCodec *codec) { guint id; g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0); g_object_get(codec, "id", &id, NULL); return id; } gchar * purple_media_codec_get_encoding_name(PurpleMediaCodec *codec) { gchar *name; g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL); g_object_get(codec, "encoding-name", &name, NULL); return name; } guint purple_media_codec_get_clock_rate(PurpleMediaCodec *codec) { guint clock_rate; g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0); g_object_get(codec, "clock-rate", &clock_rate, NULL); return clock_rate; } guint purple_media_codec_get_channels(PurpleMediaCodec *codec) { guint channels; g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0); g_object_get(codec, "channels", &channels, NULL); return channels; } GList * purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec) { GList *optional_params; g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL); g_object_get(codec, "optional-params", &optional_params, NULL); return optional_params; } void purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec, const gchar *name, const gchar *value) { #ifdef USE_VV PurpleMediaCodecPrivate *priv; PurpleKeyValuePair *new_param; g_return_if_fail(codec != NULL); g_return_if_fail(name != NULL && value != NULL); priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); new_param = g_new0(PurpleKeyValuePair, 1); new_param->key = g_strdup(name); new_param->value = g_strdup(value); priv->optional_params = g_list_append( priv->optional_params, new_param); #endif } void purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec, PurpleKeyValuePair *param) { #ifdef USE_VV PurpleMediaCodecPrivate *priv; g_return_if_fail(codec != NULL && param != NULL); priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); g_free(param->key); g_free(param->value); g_free(param); priv->optional_params = g_list_remove(priv->optional_params, param); #endif } PurpleKeyValuePair * purple_media_codec_get_optional_parameter(PurpleMediaCodec *codec, const gchar *name, const gchar *value) { #ifdef USE_VV PurpleMediaCodecPrivate *priv; GList *iter; g_return_val_if_fail(codec != NULL, NULL); g_return_val_if_fail(name != NULL, NULL); priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); for (iter = priv->optional_params; iter; iter = g_list_next(iter)) { PurpleKeyValuePair *param = iter->data; if (!g_ascii_strcasecmp(param->key, name) && (value == NULL || !g_ascii_strcasecmp(param->value, value))) return param; } #endif return NULL; } PurpleMediaCodec * purple_media_codec_new(int id, const char *encoding_name, PurpleMediaSessionType media_type, guint clock_rate) { PurpleMediaCodec *codec = g_object_new(PURPLE_TYPE_MEDIA_CODEC, "id", id, "encoding_name", encoding_name, "media_type", media_type, "clock-rate", clock_rate, NULL); return codec; } static PurpleMediaCodec * purple_media_codec_copy(PurpleMediaCodec *codec) { #ifdef USE_VV PurpleMediaCodecPrivate *priv; PurpleMediaCodec *new_codec; GList *iter; if (codec == NULL) return NULL; priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); new_codec = purple_media_codec_new(priv->id, priv->encoding_name, priv->media_type, priv->clock_rate); g_object_set(codec, "channels", priv->channels, NULL); for (iter = priv->optional_params; iter; iter = g_list_next(iter)) { PurpleKeyValuePair *param = (PurpleKeyValuePair*)iter->data; purple_media_codec_add_optional_parameter(new_codec, param->key, param->value); } return new_codec; #else return NULL; #endif } #ifdef USE_VV static FsCodec * purple_media_codec_to_fs(const PurpleMediaCodec *codec) { PurpleMediaCodecPrivate *priv; FsCodec *new_codec; GList *iter; if (codec == NULL) return NULL; priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec); new_codec = fs_codec_new(priv->id, priv->encoding_name, purple_media_to_fs_media_type(priv->media_type), priv->clock_rate); new_codec->channels = priv->channels; for (iter = priv->optional_params; iter; iter = g_list_next(iter)) { PurpleKeyValuePair *param = (PurpleKeyValuePair*)iter->data; fs_codec_add_optional_parameter(new_codec, param->key, param->value); } return new_codec; } static PurpleMediaCodec * purple_media_codec_from_fs(const FsCodec *codec) { PurpleMediaCodec *new_codec; GList *iter; if (codec == NULL) return NULL; new_codec = purple_media_codec_new(codec->id, codec->encoding_name, purple_media_from_fs(codec->media_type, FS_DIRECTION_BOTH), codec->clock_rate); g_object_set(new_codec, "channels", codec->channels, NULL); for (iter = codec->optional_params; iter; iter = g_list_next(iter)) { FsCodecParameter *param = (FsCodecParameter*)iter->data; purple_media_codec_add_optional_parameter(new_codec, param->name, param->value); } return new_codec; } #endif gchar * purple_media_codec_to_string(const PurpleMediaCodec *codec) { #ifdef USE_VV FsCodec *fscodec = purple_media_codec_to_fs(codec); gchar *str = fs_codec_to_string(fscodec); fs_codec_destroy(fscodec); return str; #else return g_strdup(""); #endif } #ifdef USE_VV static GList * purple_media_codec_list_from_fs(GList *codecs) { GList *new_list = NULL; for (; codecs; codecs = g_list_next(codecs)) { new_list = g_list_prepend(new_list, purple_media_codec_from_fs( codecs->data)); } new_list = g_list_reverse(new_list); return new_list; } static GList * purple_media_codec_list_to_fs(GList *codecs) { GList *new_list = NULL; for (; codecs; codecs = g_list_next(codecs)) { new_list = g_list_prepend(new_list, purple_media_codec_to_fs( codecs->data)); } new_list = g_list_reverse(new_list); return new_list; } #endif GList * purple_media_codec_list_copy(GList *codecs) { GList *new_list = NULL; for (; codecs; codecs = g_list_next(codecs)) { new_list = g_list_prepend(new_list, purple_media_codec_copy(codecs->data)); } new_list = g_list_reverse(new_list); return new_list; } void purple_media_codec_list_free(GList *codecs) { for (; codecs; codecs = g_list_delete_link(codecs, codecs)) { g_object_unref(codecs->data); } } #ifdef USE_VV static PurpleMediaSession* purple_media_get_session(PurpleMedia *media, const gchar *sess_id) { g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); return (PurpleMediaSession*) (media->priv->sessions) ? g_hash_table_lookup(media->priv->sessions, sess_id) : NULL; } static FsParticipant* purple_media_get_participant(PurpleMedia *media, const gchar *name) { g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); return (FsParticipant*) (media->priv->participants) ? g_hash_table_lookup(media->priv->participants, name) : NULL; } static PurpleMediaStream* purple_media_get_stream(PurpleMedia *media, const gchar *session, const gchar *participant) { GList *streams; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); streams = media->priv->streams; for (; streams; streams = g_list_next(streams)) { PurpleMediaStream *stream = streams->data; if (!strcmp(stream->session->id, session) && !strcmp(stream->participant, participant)) return stream; } return NULL; } static GList * purple_media_get_streams(PurpleMedia *media, const gchar *session, const gchar *participant) { GList *streams; GList *ret = NULL; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); streams = media->priv->streams; for (; streams; streams = g_list_next(streams)) { PurpleMediaStream *stream = streams->data; if ((session == NULL || !strcmp(stream->session->id, session)) && (participant == NULL || !strcmp(stream->participant, participant))) ret = g_list_append(ret, stream); } return ret; } static void purple_media_add_session(PurpleMedia *media, PurpleMediaSession *session) { g_return_if_fail(PURPLE_IS_MEDIA(media)); g_return_if_fail(session != NULL); if (!media->priv->sessions) { purple_debug_info("media", "Creating hash table for sessions\n"); media->priv->sessions = g_hash_table_new(g_str_hash, g_str_equal); } g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session); } static gboolean purple_media_remove_session(PurpleMedia *media, PurpleMediaSession *session) { g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); return g_hash_table_remove(media->priv->sessions, session->id); } static FsParticipant * purple_media_add_participant(PurpleMedia *media, const gchar *name) { FsParticipant *participant; GError *err = NULL; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); participant = purple_media_get_participant(media, name); if (participant) return participant; participant = fs_conference_new_participant(media->priv->conference, (gchar*)name, &err); if (err) { purple_debug_error("media", "Error creating participant: %s\n", err->message); g_error_free(err); return NULL; } if (!media->priv->participants) { purple_debug_info("media", "Creating hash table for participants\n"); media->priv->participants = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); } g_hash_table_insert(media->priv->participants, g_strdup(name), participant); return participant; } static PurpleMediaStream * purple_media_insert_stream(PurpleMediaSession *session, const gchar *name, FsStream *stream) { PurpleMediaStream *media_stream; g_return_val_if_fail(session != NULL, NULL); media_stream = g_new0(PurpleMediaStream, 1); media_stream->stream = stream; media_stream->participant = g_strdup(name); media_stream->session = session; session->media->priv->streams = g_list_append(session->media->priv->streams, media_stream); return media_stream; } static void purple_media_insert_local_candidate(PurpleMediaSession *session, const gchar *name, FsCandidate *candidate) { PurpleMediaStream *stream; g_return_if_fail(session != NULL); stream = purple_media_get_stream(session->media, session->id, name); stream->local_candidates = g_list_append(stream->local_candidates, candidate); } #endif GList * purple_media_get_session_ids(PurpleMedia *media) { #ifdef USE_VV g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); return media->priv->sessions != NULL ? g_hash_table_get_keys(media->priv->sessions) : NULL; #else return NULL; #endif } #ifdef USE_VV static void purple_media_set_src(PurpleMedia *media, const gchar *sess_id, GstElement *src) { PurpleMediaSession *session; GstPad *sinkpad; GstPad *srcpad; g_return_if_fail(PURPLE_IS_MEDIA(media)); g_return_if_fail(GST_IS_ELEMENT(src)); session = purple_media_get_session(media, sess_id); if (session == NULL) { purple_debug_warning("media", "purple_media_set_src: trying" " to set src on non-existent session\n"); return; } if (session->src) gst_object_unref(session->src); session->src = src; gst_element_set_locked_state(session->src, TRUE); session->tee = gst_element_factory_make("tee", NULL); gst_bin_add(GST_BIN(session->media->priv->confbin), session->tee); /* This supposedly isn't necessary, but it silences some warnings */ if (GST_ELEMENT_PARENT(session->media->priv->confbin) == GST_ELEMENT_PARENT(session->src)) { GstPad *pad = gst_element_get_static_pad(session->tee, "sink"); GstPad *ghost = gst_ghost_pad_new(NULL, pad); gst_object_unref(pad); gst_pad_set_active(ghost, TRUE); gst_element_add_pad(session->media->priv->confbin, ghost); } gst_element_set_state(session->tee, GST_STATE_PLAYING); gst_element_link(session->src, session->media->priv->confbin); g_object_get(session->session, "sink-pad", &sinkpad, NULL); if (session->type & PURPLE_MEDIA_SEND_AUDIO) { gchar *name = g_strdup_printf("volume_%s", session->id); GstElement *level; GstElement *volume = gst_element_factory_make("volume", name); double input_volume = purple_prefs_get_int( "/purple/media/audio/volume/input")/10.0; g_free(name); name = g_strdup_printf("sendlevel_%s", session->id); level = gst_element_factory_make("level", name); g_free(name); gst_bin_add(GST_BIN(session->media->priv->confbin), volume); gst_bin_add(GST_BIN(session->media->priv->confbin), level); gst_element_set_state(level, GST_STATE_PLAYING); gst_element_set_state(volume, GST_STATE_PLAYING); gst_element_link(volume, level); gst_element_link(session->tee, volume); srcpad = gst_element_get_static_pad(level, "src"); g_object_set(volume, "volume", input_volume, NULL); } else { srcpad = gst_element_get_request_pad(session->tee, "src%d"); } purple_debug_info("media", "connecting pad: %s\n", gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK ? "success" : "failure"); gst_element_set_locked_state(session->src, FALSE); gst_object_unref(session->src); } #endif #ifdef USE_GSTREAMER GstElement * purple_media_get_src(PurpleMedia *media, const gchar *sess_id) { #ifdef USE_VV PurpleMediaSession *session; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); session = purple_media_get_session(media, sess_id); return (session != NULL) ? session->src : NULL; #else return NULL; #endif } #endif /* USE_GSTREAMER */ #ifdef USE_VV static PurpleMediaSession * purple_media_session_from_fs_stream(PurpleMedia *media, FsStream *stream) { FsSession *fssession; GList *values; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); g_return_val_if_fail(FS_IS_STREAM(stream), NULL); g_object_get(stream, "session", &fssession, NULL); values = g_hash_table_get_values(media->priv->sessions); for (; values; values = g_list_delete_link(values, values)) { PurpleMediaSession *session = values->data; if (session->session == fssession) { g_list_free(values); g_object_unref(fssession); return session; } } g_object_unref(fssession); return NULL; } static gboolean media_bus_call(GstBus *bus, GstMessage *msg, PurpleMedia *media) { switch(GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_ELEMENT: { if (g_signal_has_handler_pending(media, purple_media_signals[LEVEL], 0, FALSE) && gst_structure_has_name( gst_message_get_structure(msg), "level")) { GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); gchar *name; gchar *participant = NULL; PurpleMediaSession *session = NULL; gdouble rms_db; gdouble percent; const GValue *list; const GValue *value; if (!PURPLE_IS_MEDIA(media) || GST_ELEMENT_PARENT(src) != media->priv->confbin) break; name = gst_element_get_name(src); if (!strncmp(name, "sendlevel_", 10)) { session = purple_media_get_session( media, name+10); } else { GList *iter = media->priv->streams; for (; iter; iter = g_list_next(iter)) { PurpleMediaStream *stream = iter->data; if (stream->level == src) { session = stream->session; participant = stream->participant; break; } } } g_free(name); if (!session) break; list = gst_structure_get_value( gst_message_get_structure(msg), "rms"); value = gst_value_list_get_value(list, 0); rms_db = g_value_get_double(value); percent = pow(10, rms_db / 20) * 5; if(percent > 1.0) percent = 1.0; g_signal_emit(media, purple_media_signals[LEVEL], 0, session->id, participant, percent); break; } if (!FS_IS_CONFERENCE(GST_MESSAGE_SRC(msg)) || !PURPLE_IS_MEDIA(media) || media->priv->conference != FS_CONFERENCE(GST_MESSAGE_SRC(msg))) break; if (gst_structure_has_name(msg->structure, "farsight-error")) { FsError error_no; gst_structure_get_enum(msg->structure, "error-no", FS_TYPE_ERROR, (gint*)&error_no); switch (error_no) { case FS_ERROR_NO_CODECS: purple_media_error(media, _("No codecs found. Install some GStreamer codecs found in GStreamer plugins packages.")); purple_media_end(media, NULL, NULL); break; case FS_ERROR_NO_CODECS_LEFT: purple_media_error(media, _("No codecs left. Your codec preferences in fs-codecs.conf are too strict.")); purple_media_end(media, NULL, NULL); break; case FS_ERROR_UNKNOWN_CNAME: /* * Unknown CName is only a problem for the * multicast transmitter which isn't used. * It is also deprecated. */ break; default: purple_debug_error("media", "farsight-error: %i: %s\n", error_no, gst_structure_get_string(msg->structure, "error-msg")); break; } if (FS_ERROR_IS_FATAL(error_no)) { purple_media_error(media, _("A non-recoverable Farsight2 error has occurred.")); purple_media_end(media, NULL, NULL); } } else if (gst_structure_has_name(msg->structure, "farsight-new-local-candidate")) { FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream")); FsCandidate *local_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "candidate")); PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream); purple_media_new_local_candidate_cb(stream, local_candidate, session); } else if (gst_structure_has_name(msg->structure, "farsight-local-candidates-prepared")) { FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream")); PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream); purple_media_candidates_prepared_cb(stream, session); } else if (gst_structure_has_name(msg->structure, "farsight-new-active-candidate-pair")) { FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream")); FsCandidate *local_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "local-candidate")); FsCandidate *remote_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "remote-candidate")); PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream); purple_media_candidate_pair_established_cb(stream, local_candidate, remote_candidate, session); } else if (gst_structure_has_name(msg->structure, "farsight-recv-codecs-changed")) { GList *codecs = g_value_get_boxed(gst_structure_get_value(msg->structure, "codecs")); FsCodec *codec = codecs->data; purple_debug_info("media", "farsight-recv-codecs-changed: %s\n", codec->encoding_name); } else if (gst_structure_has_name(msg->structure, "farsight-component-state-changed")) { FsStreamState fsstate = g_value_get_enum(gst_structure_get_value(msg->structure, "state")); guint component = g_value_get_uint(gst_structure_get_value(msg->structure, "component")); const gchar *state; switch (fsstate) { case FS_STREAM_STATE_FAILED: state = "FAILED"; break; case FS_STREAM_STATE_DISCONNECTED: state = "DISCONNECTED"; break; case FS_STREAM_STATE_GATHERING: state = "GATHERING"; break; case FS_STREAM_STATE_CONNECTING: state = "CONNECTING"; break; case FS_STREAM_STATE_CONNECTED: state = "CONNECTED"; break; case FS_STREAM_STATE_READY: state = "READY"; break; default: state = "UNKNOWN"; break; } purple_debug_info("media", "farsight-component-state-changed: component: %u state: %s\n", component, state); } else if (gst_structure_has_name(msg->structure, "farsight-send-codec-changed")) { FsCodec *codec = g_value_get_boxed(gst_structure_get_value(msg->structure, "codec")); gchar *codec_str = fs_codec_to_string(codec); purple_debug_info("media", "farsight-send-codec-changed: codec: %s\n", codec_str); g_free(codec_str); } else if (gst_structure_has_name(msg->structure, "farsight-codecs-changed")) { GList *sessions = g_hash_table_get_values(PURPLE_MEDIA(media)->priv->sessions); FsSession *fssession = g_value_get_object(gst_structure_get_value(msg->structure, "session")); for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { PurpleMediaSession *session = sessions->data; if (session->session == fssession) { gchar *session_id = g_strdup(session->id); g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, session_id); g_free(session_id); g_list_free(sessions); break; } } } break; } case GST_MESSAGE_ERROR: { GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(msg)); GstElement *lastElement = NULL; while (!GST_IS_PIPELINE(element)) { if (element == media->priv->confbin) { purple_media_error(media, _("Conference error")); purple_media_end(media, NULL, NULL); break; } lastElement = element; element = GST_ELEMENT_PARENT(element); } if (GST_IS_PIPELINE(element)) { GList *sessions = g_hash_table_get_values(media->priv->sessions); for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { PurpleMediaSession *session = sessions->data; if (session->src == lastElement) { if (session->type & PURPLE_MEDIA_AUDIO) purple_media_error(media, _("Error with your microphone")); else purple_media_error(media, _("Error with your webcam")); purple_media_end(media, NULL, NULL); break; } } g_list_free(sessions); } } default: break; } return TRUE; } #endif PurpleAccount * purple_media_get_account(PurpleMedia *media) { #ifdef USE_VV PurpleAccount *account; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); g_object_get(G_OBJECT(media), "account", &account, NULL); return account; #else return NULL; #endif } gpointer purple_media_get_prpl_data(PurpleMedia *media) { #ifdef USE_VV gpointer prpl_data; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); g_object_get(G_OBJECT(media), "prpl-data", &prpl_data, NULL); return prpl_data; #else return NULL; #endif } void purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data) { #ifdef USE_VV g_return_if_fail(PURPLE_IS_MEDIA(media)); g_object_set(G_OBJECT(media), "prpl-data", prpl_data, NULL); #endif } void purple_media_error(PurpleMedia *media, const gchar *error, ...) { #ifdef USE_VV va_list args; gchar *message; g_return_if_fail(PURPLE_IS_MEDIA(media)); va_start(args, error); message = g_strdup_vprintf(error, args); va_end(args); purple_debug_error("media", "%s\n", message); g_signal_emit(media, purple_media_signals[S_ERROR], 0, message); g_free(message); #endif } void purple_media_end(PurpleMedia *media, const gchar *session_id, const gchar *participant) { #ifdef USE_VV g_return_if_fail(PURPLE_IS_MEDIA(media)); if (session_id == NULL && participant == NULL) { g_signal_emit(media, purple_media_signals[STATE_CHANGED], 0, PURPLE_MEDIA_STATE_END, NULL, NULL); g_object_unref(media); } #endif } void purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type, const gchar *session_id, const gchar *participant, gboolean local) { #ifdef USE_VV g_return_if_fail(PURPLE_IS_MEDIA(media)); if (type == PURPLE_MEDIA_INFO_ACCEPT) { GList *streams; g_return_if_fail(PURPLE_IS_MEDIA(media)); streams = purple_media_get_streams(media, session_id, participant); for (; streams; streams = g_list_delete_link(streams, streams)) { PurpleMediaStream *stream = streams->data; g_object_set(G_OBJECT(stream->stream), "direction", purple_media_to_fs_stream_direction( stream->session->type), NULL); stream->accepted = TRUE; if (stream->remote_candidates != NULL && stream->initiator == FALSE) { GError *err = NULL; fs_stream_set_remote_candidates(stream->stream, stream->remote_candidates, &err); if (err) { purple_debug_error("media", "Error adding remote" " candidates: %s\n", err->message); g_error_free(err); } } } } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_MUTE || type == PURPLE_MEDIA_INFO_UNMUTE)) { GList *sessions; gboolean active = (type == PURPLE_MEDIA_INFO_MUTE); g_return_if_fail(PURPLE_IS_MEDIA(media)); if (session_id == NULL) sessions = g_hash_table_get_values( media->priv->sessions); else sessions = g_list_prepend(NULL, purple_media_get_session( media, session_id)); purple_debug_info("media", "Turning mute %s\n", active ? "on" : "off"); for (; sessions; sessions = g_list_delete_link( sessions, sessions)) { PurpleMediaSession *session = sessions->data; if (session->type & PURPLE_MEDIA_SEND_AUDIO) { gchar *name = g_strdup_printf("volume_%s", session->id); GstElement *volume = gst_bin_get_by_name( GST_BIN(session->media-> priv->confbin), name); g_free(name); g_object_set(volume, "mute", active, NULL); } } } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_PAUSE || type == PURPLE_MEDIA_INFO_UNPAUSE)) { gboolean active = (type == PURPLE_MEDIA_INFO_PAUSE); GList *streams = purple_media_get_streams(media, session_id, participant); for (; streams; streams = g_list_delete_link(streams, streams)) { PurpleMediaStream *stream = streams->data; if (stream->session->type & PURPLE_MEDIA_SEND_VIDEO) { stream->paused = active; if (!stream->held) g_object_set(stream->stream, "direction", purple_media_to_fs_stream_direction( stream->session->type & ((active) ? ~PURPLE_MEDIA_SEND_VIDEO : PURPLE_MEDIA_VIDEO)), NULL); } } } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_HOLD || type == PURPLE_MEDIA_INFO_UNHOLD)) { GList *streams; gboolean active = (type == PURPLE_MEDIA_INFO_HOLD); g_return_if_fail(PURPLE_IS_MEDIA(media)); streams = purple_media_get_streams(media, session_id, participant); for (; streams; streams = g_list_delete_link(streams, streams)) { PurpleMediaStream *stream = streams->data; stream->held = active; if (stream->session->type & PURPLE_MEDIA_VIDEO) { FsStreamDirection direction; direction = ((active) ? ~PURPLE_MEDIA_VIDEO : PURPLE_MEDIA_VIDEO); if (!active && stream->paused) direction &= ~PURPLE_MEDIA_SEND_VIDEO; g_object_set(stream->stream, "direction", purple_media_to_fs_stream_direction( stream->session->type & direction), NULL); } else if (stream->session->type & PURPLE_MEDIA_AUDIO) { g_object_set(stream->stream, "direction", purple_media_to_fs_stream_direction( stream->session->type & ((active) ? ~PURPLE_MEDIA_AUDIO : PURPLE_MEDIA_AUDIO)), NULL); } } } g_signal_emit(media, purple_media_signals[STREAM_INFO], 0, type, session_id, participant, local); if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) { purple_media_end(media, session_id, participant); } #endif } #ifdef USE_VV static void purple_media_new_local_candidate_cb(FsStream *stream, FsCandidate *local_candidate, PurpleMediaSession *session) { gchar *name; FsParticipant *participant; PurpleMediaCandidate *candidate; g_return_if_fail(FS_IS_STREAM(stream)); g_return_if_fail(session != NULL); purple_debug_info("media", "got new local candidate: %s\n", local_candidate->foundation); g_object_get(stream, "participant", &participant, NULL); g_object_get(participant, "cname", &name, NULL); g_object_unref(participant); purple_media_insert_local_candidate(session, name, fs_candidate_copy(local_candidate)); candidate = purple_media_candidate_from_fs(local_candidate); g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE], 0, session->id, name, candidate); g_object_unref(candidate); g_free(name); } static void purple_media_candidates_prepared_cb(FsStream *stream, PurpleMediaSession *session) { gchar *name; FsParticipant *participant; PurpleMediaStream *stream_data; g_return_if_fail(FS_IS_STREAM(stream)); g_return_if_fail(session != NULL); g_object_get(stream, "participant", &participant, NULL); g_object_get(participant, "cname", &name, NULL); g_object_unref(participant); stream_data = purple_media_get_stream(session->media, session->id, name); stream_data->candidates_prepared = TRUE; g_signal_emit(session->media, purple_media_signals[CANDIDATES_PREPARED], 0, session->id, name); g_free(name); } /* callback called when a pair of transport candidates (local and remote) * has been established */ static void purple_media_candidate_pair_established_cb(FsStream *fsstream, FsCandidate *native_candidate, FsCandidate *remote_candidate, PurpleMediaSession *session) { gchar *name; FsParticipant *participant; PurpleMediaStream *stream; GList *iter; g_return_if_fail(FS_IS_STREAM(fsstream)); g_return_if_fail(session != NULL); g_object_get(fsstream, "participant", &participant, NULL); g_object_get(participant, "cname", &name, NULL); g_object_unref(participant); stream = purple_media_get_stream(session->media, session->id, name); iter = stream->active_local_candidates; for(; iter; iter = g_list_next(iter)) { FsCandidate *c = iter->data; if (native_candidate->component_id == c->component_id) { fs_candidate_destroy(c); stream->active_local_candidates = g_list_delete_link(iter, iter); stream->active_local_candidates = g_list_prepend( stream->active_local_candidates, fs_candidate_copy(native_candidate)); break; } } if (iter == NULL) stream->active_local_candidates = g_list_prepend( stream->active_local_candidates, fs_candidate_copy(native_candidate)); iter = stream->active_remote_candidates; for(; iter; iter = g_list_next(iter)) { FsCandidate *c = iter->data; if (native_candidate->component_id == c->component_id) { fs_candidate_destroy(c); stream->active_remote_candidates = g_list_delete_link(iter, iter); stream->active_remote_candidates = g_list_prepend( stream->active_remote_candidates, fs_candidate_copy(remote_candidate)); break; } } if (iter == NULL) stream->active_remote_candidates = g_list_prepend( stream->active_remote_candidates, fs_candidate_copy(remote_candidate)); purple_debug_info("media", "candidate pair established\n"); } static gboolean purple_media_connected_cb(PurpleMediaStream *stream) { g_return_val_if_fail(stream != NULL, FALSE); stream->connected_cb_id = 0; purple_media_manager_create_output_window( stream->session->media->priv->manager, stream->session->media, stream->session->id, stream->participant); g_signal_emit(stream->session->media, purple_media_signals[STATE_CHANGED], 0, PURPLE_MEDIA_STATE_CONNECTED, stream->session->id, stream->participant); return FALSE; } static void purple_media_src_pad_added_cb(FsStream *fsstream, GstPad *srcpad, FsCodec *codec, PurpleMediaStream *stream) { PurpleMediaPrivate *priv; GstPad *sinkpad; g_return_if_fail(FS_IS_STREAM(fsstream)); g_return_if_fail(stream != NULL); priv = stream->session->media->priv; if (stream->src == NULL) { GstElement *sink = NULL; if (codec->media_type == FS_MEDIA_TYPE_AUDIO) { GstElement *queue = NULL; double output_volume = purple_prefs_get_int( "/purple/media/audio/volume/output")/10.0; /* * Should this instead be: * audioconvert ! audioresample ! liveadder ! * audioresample ! audioconvert ! realsink */ queue = gst_element_factory_make("queue", NULL); stream->volume = gst_element_factory_make( "volume", NULL); g_object_set(stream->volume, "volume", output_volume, NULL); stream->level = gst_element_factory_make( "level", NULL); stream->src = gst_element_factory_make( "liveadder", NULL); sink = purple_media_manager_get_element(priv->manager, PURPLE_MEDIA_RECV_AUDIO, stream->session->media, stream->session->id, stream->participant); gst_bin_add(GST_BIN(priv->confbin), queue); gst_bin_add(GST_BIN(priv->confbin), stream->volume); gst_bin_add(GST_BIN(priv->confbin), stream->level); gst_bin_add(GST_BIN(priv->confbin), sink); gst_element_set_state(sink, GST_STATE_PLAYING); gst_element_set_state(stream->level, GST_STATE_PLAYING); gst_element_set_state(stream->volume, GST_STATE_PLAYING); gst_element_set_state(queue, GST_STATE_PLAYING); gst_element_link(stream->level, sink); gst_element_link(stream->volume, stream->level); gst_element_link(queue, stream->volume); sink = queue; } else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) { stream->src = gst_element_factory_make( "fsfunnel", NULL); sink = gst_element_factory_make( "fakesink", NULL); g_object_set(G_OBJECT(sink), "async", FALSE, NULL); gst_bin_add(GST_BIN(priv->confbin), sink); gst_element_set_state(sink, GST_STATE_PLAYING); } stream->tee = gst_element_factory_make("tee", NULL); gst_bin_add_many(GST_BIN(priv->confbin), stream->src, stream->tee, NULL); gst_element_set_state(stream->tee, GST_STATE_PLAYING); gst_element_set_state(stream->src, GST_STATE_PLAYING); gst_element_link_many(stream->src, stream->tee, sink, NULL); } sinkpad = gst_element_get_request_pad(stream->src, "sink%d"); gst_pad_link(srcpad, sinkpad); gst_object_unref(sinkpad); stream->connected_cb_id = purple_timeout_add(0, (GSourceFunc)purple_media_connected_cb, stream); } static void purple_media_element_added_cb(FsElementAddedNotifier *self, GstBin *bin, GstElement *element, gpointer user_data) { /* * Hack to make H264 work with Gmail video. */ if (!strncmp(GST_ELEMENT_NAME(element), "x264", 4)) { g_object_set(GST_OBJECT(element), "cabac", FALSE, NULL); } } #endif /* USE_VV */ gboolean purple_media_add_stream(PurpleMedia *media, const gchar *sess_id, const gchar *who, PurpleMediaSessionType type, gboolean initiator, const gchar *transmitter, guint num_params, GParameter *params) { #ifdef USE_VV PurpleMediaSession *session; FsParticipant *participant = NULL; PurpleMediaStream *stream = NULL; FsMediaType media_type = purple_media_to_fs_media_type(type); FsStreamDirection type_direction = purple_media_to_fs_stream_direction(type); gboolean is_nice = !strcmp(transmitter, "nice"); g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); session = purple_media_get_session(media, sess_id); if (!session) { GError *err = NULL; GList *codec_conf = NULL, *iter = NULL; gchar *filename = NULL; PurpleMediaSessionType session_type; GstElement *src = NULL; session = g_new0(PurpleMediaSession, 1); session->session = fs_conference_new_session( media->priv->conference, media_type, &err); if (err != NULL) { purple_media_error(media, _("Error creating session: %s"), err->message); g_error_free(err); g_free(session); return FALSE; } filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL); codec_conf = fs_codec_list_from_keyfile(filename, &err); g_free(filename); if (err != NULL) { if (err->code == 4) purple_debug_info("media", "Couldn't read " "fs-codec.conf: %s\n", err->message); else purple_debug_error("media", "Error reading " "fs-codec.conf: %s\n", err->message); g_error_free(err); } /* * Add SPEEX if the configuration file doesn't exist or * there isn't a speex entry. */ for (iter = codec_conf; iter; iter = g_list_next(iter)) { FsCodec *codec = iter->data; if (!g_ascii_strcasecmp(codec->encoding_name, "speex")) break; } if (iter == NULL) { codec_conf = g_list_prepend(codec_conf, fs_codec_new(FS_CODEC_ID_ANY, "SPEEX", FS_MEDIA_TYPE_AUDIO, 8000)); codec_conf = g_list_prepend(codec_conf, fs_codec_new(FS_CODEC_ID_ANY, "SPEEX", FS_MEDIA_TYPE_AUDIO, 16000)); } fs_session_set_codec_preferences(session->session, codec_conf, NULL); /* * Removes a 5-7 second delay before * receiving the src-pad-added signal. * Only works for non-multicast FsRtpSessions. */ if (is_nice || !strcmp(transmitter, "rawudp")) g_object_set(G_OBJECT(session->session), "no-rtcp-timeout", 0, NULL); /* * Hack to make x264 work with Gmail video. */ if (is_nice && !strcmp(sess_id, "google-video")) { FsElementAddedNotifier *notifier = fs_element_added_notifier_new(); g_signal_connect(G_OBJECT(notifier), "element-added", G_CALLBACK(purple_media_element_added_cb), stream); fs_element_added_notifier_add(notifier, GST_BIN(media->priv->conference)); } fs_codec_list_destroy(codec_conf); session->id = g_strdup(sess_id); session->media = media; session->type = type; session->initiator = initiator; purple_media_add_session(media, session); g_signal_emit(media, purple_media_signals[STATE_CHANGED], 0, PURPLE_MEDIA_STATE_NEW, session->id, NULL); if (type_direction & FS_DIRECTION_SEND) { session_type = purple_media_from_fs(media_type, FS_DIRECTION_SEND); src = purple_media_manager_get_element( media->priv->manager, session_type, media, session->id, who); if (!GST_IS_ELEMENT(src)) { purple_debug_error("media", "Error creating src for session %s\n", session->id); purple_media_end(media, session->id, NULL); return FALSE; } purple_media_set_src(media, session->id, src); gst_element_set_state(session->src, GST_STATE_PLAYING); purple_media_manager_create_output_window( media->priv->manager, session->media, session->id, NULL); } } if (!(participant = purple_media_add_participant(media, who))) { purple_media_remove_session(media, session); g_free(session); return FALSE; } else { g_signal_emit(media, purple_media_signals[STATE_CHANGED], 0, PURPLE_MEDIA_STATE_NEW, NULL, who); } stream = purple_media_get_stream(media, sess_id, who); if (!stream) { GError *err = NULL; FsStream *fsstream = NULL; const gchar *stun_ip = purple_network_get_stun_ip(); const gchar *turn_ip = purple_network_get_turn_ip(); guint new_num_params = !stun_ip && !turn_ip ? num_params + 1 : (stun_ip && is_nice) && turn_ip ? num_params + 3 : num_params + 2; guint next_param_index = num_params; GParameter *param = g_new0(GParameter, new_num_params); memcpy(param, params, sizeof(GParameter) * num_params); /* set controlling mode according to direction */ param[next_param_index].name = "controlling-mode"; g_value_init(¶m[next_param_index].value, G_TYPE_BOOLEAN); g_value_set_boolean(¶m[next_param_index].value, initiator); next_param_index++; if (stun_ip || turn_ip) { if (stun_ip) { purple_debug_info("media", "setting property stun-ip on new stream: %s\n", stun_ip); param[next_param_index].name = "stun-ip"; g_value_init(¶m[next_param_index].value, G_TYPE_STRING); g_value_set_string(¶m[next_param_index].value, stun_ip); next_param_index++; } if (turn_ip && is_nice) { GValueArray *relay_info = g_value_array_new(0); GValue value; gint turn_port = purple_prefs_get_int("/purple/network/turn_port"); const gchar *username = purple_prefs_get_string("/purple/network/turn_username"); const gchar *password = purple_prefs_get_string("/purple/network/turn_password"); GstStructure *turn_setup = gst_structure_new("relay-info", "ip", G_TYPE_STRING, turn_ip, "port", G_TYPE_UINT, turn_port, "username", G_TYPE_STRING, username, "password", G_TYPE_STRING, password, NULL); if (turn_setup) { memset(&value, 0, sizeof(GValue)); g_value_init(&value, GST_TYPE_STRUCTURE); gst_value_set_structure(&value, turn_setup); relay_info = g_value_array_append(relay_info, &value); gst_structure_free(turn_setup); purple_debug_info("media", "setting property relay-info on new stream\n"); param[next_param_index].name = "relay-info"; g_value_init(¶m[next_param_index].value, G_TYPE_VALUE_ARRAY); g_value_set_boxed(¶m[next_param_index].value, relay_info); g_value_array_free(relay_info); } else { purple_debug_error("media", "Error relay info"); g_object_unref(participant); g_hash_table_remove(media->priv->participants, who); purple_media_remove_session(media, session); g_free(session); return FALSE; } } } fsstream = fs_session_new_stream(session->session, participant, initiator == TRUE ? type_direction : (type_direction & FS_DIRECTION_RECV), transmitter, new_num_params, param, &err); g_free(param); if (fsstream == NULL) { purple_debug_error("media", "Error creating stream: %s\n", err && err->message ? err->message : "NULL"); if (err) g_error_free(err); g_object_unref(participant); g_hash_table_remove(media->priv->participants, who); purple_media_remove_session(media, session); g_free(session); return FALSE; } stream = purple_media_insert_stream(session, who, fsstream); stream->initiator = initiator; /* callback for source pad added (new stream source ready) */ g_signal_connect(G_OBJECT(fsstream), "src-pad-added", G_CALLBACK(purple_media_src_pad_added_cb), stream); g_signal_emit(media, purple_media_signals[STATE_CHANGED], 0, PURPLE_MEDIA_STATE_NEW, session->id, who); } else { if (purple_media_to_fs_stream_direction(stream->session->type) != type_direction) { /* change direction */ g_object_set(stream->stream, "direction", type_direction, NULL); } } return TRUE; #else return FALSE; #endif /* USE_VV */ } PurpleMediaManager * purple_media_get_manager(PurpleMedia *media) { PurpleMediaManager *ret; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); g_object_get(media, "manager", &ret, NULL); return ret; } PurpleMediaSessionType purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id) { #ifdef USE_VV PurpleMediaSession *session; g_return_val_if_fail(PURPLE_IS_MEDIA(media), PURPLE_MEDIA_NONE); session = purple_media_get_session(media, sess_id); return session->type; #else return PURPLE_MEDIA_NONE; #endif } /* XXX: Should wait until codecs-ready is TRUE before using this function */ GList * purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id) { #ifdef USE_VV GList *fscodecs; GList *codecs; PurpleMediaSession *session; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); session = purple_media_get_session(media, sess_id); if (session == NULL) return NULL; g_object_get(G_OBJECT(session->session), "codecs", &fscodecs, NULL); codecs = purple_media_codec_list_from_fs(fscodecs); fs_codec_list_destroy(fscodecs); return codecs; #else return NULL; #endif } GList * purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id, const gchar *participant) { #ifdef USE_VV PurpleMediaStream *stream; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); stream = purple_media_get_stream(media, sess_id, participant); return stream ? purple_media_candidate_list_from_fs( stream->local_candidates) : NULL; #else return NULL; #endif } void purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id, const gchar *participant, GList *remote_candidates) { #ifdef USE_VV PurpleMediaStream *stream; GError *err = NULL; g_return_if_fail(PURPLE_IS_MEDIA(media)); stream = purple_media_get_stream(media, sess_id, participant); if (stream == NULL) { purple_debug_error("media", "purple_media_add_remote_candidates: " "couldn't find stream %s %s.\n", sess_id ? sess_id : "(null)", participant ? participant : "(null)"); return; } stream->remote_candidates = g_list_concat(stream->remote_candidates, purple_media_candidate_list_to_fs(remote_candidates)); if (stream->initiator == TRUE || stream->accepted == TRUE) { fs_stream_set_remote_candidates(stream->stream, stream->remote_candidates, &err); if (err) { purple_debug_error("media", "Error adding remote" " candidates: %s\n", err->message); g_error_free(err); } } #endif } #if 0 /* * These two functions aren't being used and I'd rather not lock in the API * until they are needed. If they ever are. */ GList * purple_media_get_active_local_candidates(PurpleMedia *media, const gchar *sess_id, const gchar *participant) { #ifdef USE_VV PurpleMediaStream *stream; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); stream = purple_media_get_stream(media, sess_id, participant); return purple_media_candidate_list_from_fs( stream->active_local_candidates); #else return NULL; #endif } GList * purple_media_get_active_remote_candidates(PurpleMedia *media, const gchar *sess_id, const gchar *participant) { #ifdef USE_VV PurpleMediaStream *stream; g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); stream = purple_media_get_stream(media, sess_id, participant); return purple_media_candidate_list_from_fs( stream->active_remote_candidates); #else return NULL; #endif } #endif gboolean purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id, const gchar *participant, GList *codecs) { #ifdef USE_VV PurpleMediaStream *stream; FsStream *fsstream; GList *fscodecs; GError *err = NULL; g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); stream = purple_media_get_stream(media, sess_id, participant); if (stream == NULL) return FALSE; fsstream = stream->stream; fscodecs = purple_media_codec_list_to_fs(codecs); fs_stream_set_remote_codecs(fsstream, fscodecs, &err); fs_codec_list_destroy(fscodecs); if (err) { purple_debug_error("media", "Error setting remote codecs: %s\n", err->message); g_error_free(err); return FALSE; } return TRUE; #else return FALSE; #endif } gboolean purple_media_candidates_prepared(PurpleMedia *media, const gchar *session_id, const gchar *participant) { #ifdef USE_VV GList *streams; gboolean prepared = TRUE; g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); streams = purple_media_get_streams(media, session_id, participant); for (; streams; streams = g_list_delete_link(streams, streams)) { PurpleMediaStream *stream = streams->data; if (stream->candidates_prepared == FALSE) { g_list_free(streams); prepared = FALSE; break; } } return prepared; #else return FALSE; #endif } gboolean purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec) { #ifdef USE_VV PurpleMediaSession *session; FsCodec *fscodec; GError *err = NULL; g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); session = purple_media_get_session(media, sess_id); if (session != NULL) return FALSE; fscodec = purple_media_codec_to_fs(codec); fs_session_set_send_codec(session->session, fscodec, &err); fs_codec_destroy(fscodec); if (err) { purple_debug_error("media", "Error setting send codec\n"); g_error_free(err); return FALSE; } return TRUE; #else return FALSE; #endif } gboolean purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id) { #ifdef USE_VV gboolean ret; g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); if (sess_id != NULL) { PurpleMediaSession *session; session = purple_media_get_session(media, sess_id); if (session == NULL) return FALSE; if (session->type & (PURPLE_MEDIA_SEND_AUDIO | PURPLE_MEDIA_SEND_VIDEO)) g_object_get(session->session, "codecs-ready", &ret, NULL); else ret = TRUE; } else { GList *values = g_hash_table_get_values(media->priv->sessions); for (; values; values = g_list_delete_link(values, values)) { PurpleMediaSession *session = values->data; if (session->type & (PURPLE_MEDIA_SEND_AUDIO | PURPLE_MEDIA_SEND_VIDEO)) g_object_get(session->session, "codecs-ready", &ret, NULL); else ret = TRUE; if (ret == FALSE) break; } if (values != NULL) g_list_free(values); } return ret; #else return FALSE; #endif } gboolean purple_media_is_initiator(PurpleMedia *media, const gchar *sess_id, const gchar *participant) { #ifdef USE_VV g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); if (sess_id == NULL && participant == NULL) return media->priv->initiator; else if (sess_id != NULL && participant == NULL) { PurpleMediaSession *session = purple_media_get_session(media, sess_id); return session != NULL ? session->initiator : FALSE; } else if (sess_id != NULL && participant != NULL) { PurpleMediaStream *stream = purple_media_get_stream( media, sess_id, participant); return stream != NULL ? stream->initiator : FALSE; } #endif return FALSE; } gboolean purple_media_accepted(PurpleMedia *media, const gchar *sess_id, const gchar *participant) { #ifdef USE_VV gboolean accepted = TRUE; g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); if (sess_id == NULL && participant == NULL) { GList *streams = media->priv->streams; for (; streams; streams = g_list_next(streams)) { PurpleMediaStream *stream = streams->data; if (stream->accepted == FALSE) { accepted = FALSE; break; } } } else if (sess_id != NULL && participant == NULL) { GList *streams = purple_media_get_streams( media, sess_id, NULL); for (; streams; streams = g_list_delete_link(streams, streams)) { PurpleMediaStream *stream = streams->data; if (stream->accepted == FALSE) { g_list_free(streams); accepted = FALSE; break; } } } else if (sess_id != NULL && participant != NULL) { PurpleMediaStream *stream = purple_media_get_stream( media, sess_id, participant); if (stream == NULL || stream->accepted == FALSE) accepted = FALSE; } return accepted; #else return FALSE; #endif } void purple_media_set_input_volume(PurpleMedia *media, const gchar *session_id, double level) { #ifdef USE_VV GList *sessions; g_return_if_fail(PURPLE_IS_MEDIA(media)); purple_prefs_set_int("/purple/media/audio/volume/input", level); if (session_id == NULL) sessions = g_hash_table_get_values(media->priv->sessions); else sessions = g_list_append(NULL, purple_media_get_session(media, session_id)); for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { PurpleMediaSession *session = sessions->data; if (session->type & PURPLE_MEDIA_SEND_AUDIO) { gchar *name = g_strdup_printf("volume_%s", session->id); GstElement *volume = gst_bin_get_by_name( GST_BIN(session->media->priv->confbin), name); g_free(name); g_object_set(volume, "volume", level/10.0, NULL); } } #endif } void purple_media_set_output_volume(PurpleMedia *media, const gchar *session_id, const gchar *participant, double level) { #ifdef USE_VV GList *streams; g_return_if_fail(PURPLE_IS_MEDIA(media)); purple_prefs_set_int("/purple/media/audio/volume/output", level); streams = purple_media_get_streams(media, session_id, participant); for (; streams; streams = g_list_delete_link(streams, streams)) { PurpleMediaStream *stream = streams->data; if (stream->session->type & PURPLE_MEDIA_RECV_AUDIO && GST_IS_ELEMENT(stream->volume)) { g_object_set(stream->volume, "volume", level/10.0, NULL); } } #endif } gulong purple_media_set_output_window(PurpleMedia *media, const gchar *session_id, const gchar *participant, gulong window_id) { #ifdef USE_VV g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); return purple_media_manager_set_output_window(media->priv->manager, media, session_id, participant, window_id); #else return 0; #endif } void purple_media_remove_output_windows(PurpleMedia *media) { #ifdef USE_VV GList *iter = media->priv->streams; for (; iter; iter = g_list_next(iter)) { PurpleMediaStream *stream = iter->data; purple_media_manager_remove_output_windows( media->priv->manager, media, stream->session->id, stream->participant); } iter = purple_media_get_session_ids(media); for (; iter; iter = g_list_delete_link(iter, iter)) { gchar *session_name = iter->data; purple_media_manager_remove_output_windows( media->priv->manager, media, session_name, NULL); } #endif } #ifdef USE_GSTREAMER GstElement * purple_media_get_tee(PurpleMedia *media, const gchar *session_id, const gchar *participant) { #ifdef USE_VV g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); if (session_id != NULL && participant == NULL) { PurpleMediaSession *session = purple_media_get_session(media, session_id); return (session != NULL) ? session->tee : NULL; } else if (session_id != NULL && participant != NULL) { PurpleMediaStream *stream = purple_media_get_stream(media, session_id, participant); return (stream != NULL) ? stream->tee : NULL; } g_return_val_if_reached(NULL); #else return NULL; #endif } #endif /* USE_GSTREAMER */