Mercurial > pidgin.yaz
diff libpurple/mediamanager.c @ 29471:d83ee160ffb6
propagate from branch 'im.pidgin.pidgin' (head eb9385f349a20856b9d3f9911dbc8024caa44052)
to branch 'im.pidgin.pidgin.next.minor' (head 439fb2dd7a285d9ca645f65f36ef0f037abe7311)
author | Elliott Sales de Andrade <qulogic@pidgin.im> |
---|---|
date | Wed, 19 Aug 2009 00:46:04 +0000 |
parents | f77978e6968e |
children | 8c991e09efcb |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/mediamanager.c Wed Aug 19 00:46:04 2009 +0000 @@ -0,0 +1,1187 @@ +/** + * @file mediamanager.c Media Manager 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal.h" + +#include "account.h" +#include "debug.h" +#include "media.h" +#include "mediamanager.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> +#include <gst/interfaces/xoverlay.h> + +/** @copydoc _PurpleMediaManagerPrivate */ +typedef struct _PurpleMediaManagerPrivate PurpleMediaManagerPrivate; +/** @copydoc _PurpleMediaOutputWindow */ +typedef struct _PurpleMediaOutputWindow PurpleMediaOutputWindow; +/** @copydoc _PurpleMediaManagerPrivate */ +typedef struct _PurpleMediaElementInfoPrivate PurpleMediaElementInfoPrivate; + +/** The media manager class. */ +struct _PurpleMediaManagerClass +{ + GObjectClass parent_class; /**< The parent class. */ +}; + +/** The media manager's data. */ +struct _PurpleMediaManager +{ + GObject parent; /**< The parent of this manager. */ + PurpleMediaManagerPrivate *priv; /**< Private data for the manager. */ +}; + +struct _PurpleMediaOutputWindow +{ + gulong id; + PurpleMedia *media; + gchar *session_id; + gchar *participant; + gulong window_id; + GstElement *sink; +}; + +struct _PurpleMediaManagerPrivate +{ + GstElement *pipeline; + PurpleMediaCaps ui_caps; + GList *medias; + GList *elements; + GList *output_windows; + gulong next_output_window_id; + + PurpleMediaElementInfo *video_src; + PurpleMediaElementInfo *video_sink; + PurpleMediaElementInfo *audio_src; + PurpleMediaElementInfo *audio_sink; +}; + +#define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate)) +#define PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfoPrivate)) + +static void purple_media_manager_class_init (PurpleMediaManagerClass *klass); +static void purple_media_manager_init (PurpleMediaManager *media); +static void purple_media_manager_finalize (GObject *object); + +static GObjectClass *parent_class = NULL; + + + +enum { + INIT_MEDIA, + LAST_SIGNAL +}; +static guint purple_media_manager_signals[LAST_SIGNAL] = {0}; +#endif + +GType +purple_media_manager_get_type() +{ +#ifdef USE_VV + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(PurpleMediaManagerClass), + NULL, + NULL, + (GClassInitFunc) purple_media_manager_class_init, + NULL, + NULL, + sizeof(PurpleMediaManager), + 0, + (GInstanceInitFunc) purple_media_manager_init, + NULL + }; + type = g_type_register_static(G_TYPE_OBJECT, "PurpleMediaManager", &info, 0); + } + return type; +#else + return G_TYPE_NONE; +#endif +} + +#ifdef USE_VV +static void +purple_media_manager_class_init (PurpleMediaManagerClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = purple_media_manager_finalize; + + purple_media_manager_signals[INIT_MEDIA] = g_signal_new ("init-media", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + purple_smarshal_BOOLEAN__OBJECT_POINTER_STRING, + G_TYPE_BOOLEAN, 3, PURPLE_TYPE_MEDIA, + G_TYPE_POINTER, G_TYPE_STRING); + g_type_class_add_private(klass, sizeof(PurpleMediaManagerPrivate)); +} + +static void +purple_media_manager_init (PurpleMediaManager *media) +{ + media->priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media); + media->priv->medias = NULL; + media->priv->next_output_window_id = 1; + + purple_prefs_add_none("/purple/media"); + purple_prefs_add_none("/purple/media/audio"); + purple_prefs_add_none("/purple/media/audio/volume"); + purple_prefs_add_int("/purple/media/audio/volume/input", 10); + purple_prefs_add_int("/purple/media/audio/volume/output", 10); +} + +static void +purple_media_manager_finalize (GObject *media) +{ + PurpleMediaManagerPrivate *priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media); + for (; priv->medias; priv->medias = + g_list_delete_link(priv->medias, priv->medias)) { + g_object_unref(priv->medias->data); + } + for (; priv->elements; priv->elements = + g_list_delete_link(priv->elements, priv->elements)) { + g_object_unref(priv->elements->data); + } + parent_class->finalize(media); +} +#endif + +PurpleMediaManager * +purple_media_manager_get() +{ +#ifdef USE_VV + static PurpleMediaManager *manager = NULL; + + if (manager == NULL) + manager = PURPLE_MEDIA_MANAGER(g_object_new(purple_media_manager_get_type(), NULL)); + return manager; +#else + return NULL; +#endif +} + +#ifdef USE_VV +static gboolean +pipeline_bus_call(GstBus *bus, GstMessage *msg, PurpleMediaManager *manager) +{ + switch(GST_MESSAGE_TYPE(msg)) { + case GST_MESSAGE_EOS: + purple_debug_info("mediamanager", "End of Stream\n"); + break; + case GST_MESSAGE_ERROR: { + gchar *debug = NULL; + GError *err = NULL; + + gst_message_parse_error(msg, &err, &debug); + + purple_debug_error("mediamanager", + "gst pipeline error: %s\n", + err->message); + g_error_free(err); + + if (debug) { + purple_debug_error("mediamanager", + "Debug details: %s\n", debug); + g_free (debug); + } + break; + } + default: + break; + } + return TRUE; +} +#endif + +#ifdef USE_GSTREAMER +GstElement * +purple_media_manager_get_pipeline(PurpleMediaManager *manager) +{ +#ifdef USE_VV + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL); + + if (manager->priv->pipeline == NULL) { + FsElementAddedNotifier *notifier; + gchar *filename; + GError *err = NULL; + GKeyFile *keyfile; + GstBus *bus; + manager->priv->pipeline = gst_pipeline_new(NULL); + + bus = gst_pipeline_get_bus( + GST_PIPELINE(manager->priv->pipeline)); + gst_bus_add_signal_watch(GST_BUS(bus)); + g_signal_connect(G_OBJECT(bus), "message", + G_CALLBACK(pipeline_bus_call), manager); + gst_bus_set_sync_handler(bus, + gst_bus_sync_signal_handler, NULL); + gst_object_unref(bus); + + filename = g_build_filename(purple_user_dir(), + "fs-element.conf", NULL); + keyfile = g_key_file_new(); + if (!g_key_file_load_from_file(keyfile, filename, + G_KEY_FILE_NONE, &err)) { + if (err->code == 4) + purple_debug_info("mediamanager", + "Couldn't read " + "fs-element.conf: %s\n", + err->message); + else + purple_debug_error("mediamanager", + "Error reading " + "fs-element.conf: %s\n", + err->message); + g_error_free(err); + } + g_free(filename); + + /* Hack to make alsasrc stop messing up audio timestamps */ + if (!g_key_file_has_key(keyfile, + "alsasrc", "slave-method", NULL)) { + g_key_file_set_integer(keyfile, + "alsasrc", "slave-method", 2); + } + + notifier = fs_element_added_notifier_new(); + fs_element_added_notifier_add(notifier, + GST_BIN(manager->priv->pipeline)); + fs_element_added_notifier_set_properties_from_keyfile( + notifier, keyfile); + + gst_element_set_state(manager->priv->pipeline, + GST_STATE_PLAYING); + } + + return manager->priv->pipeline; +#else + return NULL; +#endif +} +#endif /* USE_GSTREAMER */ + +PurpleMedia * +purple_media_manager_create_media(PurpleMediaManager *manager, + PurpleAccount *account, + const char *conference_type, + const char *remote_user, + gboolean initiator) +{ +#ifdef USE_VV + PurpleMedia *media; + FsConference *conference = FS_CONFERENCE(gst_element_factory_make(conference_type, NULL)); + GstStateChangeReturn ret; + gboolean signal_ret; + + if (conference == NULL) { + purple_conv_present_error(remote_user, account, + _("Error creating conference.")); + purple_debug_error("media", "Conference == NULL\n"); + return NULL; + } + + media = PURPLE_MEDIA(g_object_new(purple_media_get_type(), + "manager", manager, + "account", account, + "conference", conference, + "initiator", initiator, + NULL)); + + ret = gst_element_set_state(GST_ELEMENT(conference), GST_STATE_PLAYING); + + if (ret == GST_STATE_CHANGE_FAILURE) { + purple_conv_present_error(remote_user, account, + _("Error creating conference.")); + purple_debug_error("media", "Failed to start conference.\n"); + g_object_unref(media); + return NULL; + } + + g_signal_emit(manager, purple_media_manager_signals[INIT_MEDIA], 0, + media, account, remote_user, &signal_ret); + + if (signal_ret == FALSE) { + g_object_unref(media); + return NULL; + } + + manager->priv->medias = g_list_append(manager->priv->medias, media); + return media; +#else + return NULL; +#endif +} + +GList * +purple_media_manager_get_media(PurpleMediaManager *manager) +{ +#ifdef USE_VV + return manager->priv->medias; +#else + return NULL; +#endif +} + +GList * +purple_media_manager_get_media_by_account(PurpleMediaManager *manager, + PurpleAccount *account) +{ +#ifdef USE_VV + GList *media = NULL; + GList *iter; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL); + + iter = manager->priv->medias; + for (; iter; iter = g_list_next(iter)) { + if (purple_media_get_account(iter->data) == account) { + media = g_list_prepend(media, iter->data); + } + } + + return media; +#else + return NULL; +#endif +} + +void +purple_media_manager_remove_media(PurpleMediaManager *manager, + PurpleMedia *media) +{ +#ifdef USE_VV + GList *list = g_list_find(manager->priv->medias, media); + if (list) + manager->priv->medias = + g_list_delete_link(manager->priv->medias, list); +#endif +} + +#ifdef USE_VV +static void +request_pad_unlinked_cb(GstPad *pad, GstPad *peer, gpointer user_data) +{ + GstElement *parent = GST_ELEMENT_PARENT(pad); + GstIterator *iter; + GstPad *remaining_pad; + GstIteratorResult result; + + gst_element_release_request_pad(GST_ELEMENT_PARENT(pad), pad); + iter = gst_element_iterate_src_pads(parent); + + result = gst_iterator_next(iter, (gpointer)&remaining_pad); + + if (result == GST_ITERATOR_DONE) { + gst_element_set_locked_state(parent, TRUE); + gst_element_set_state(parent, GST_STATE_NULL); + gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(parent)), parent); + } else if (result == GST_ITERATOR_OK) { + gst_object_unref(remaining_pad); + } + + gst_iterator_free(iter); +} +#endif + +#ifdef USE_GSTREAMER +GstElement * +purple_media_manager_get_element(PurpleMediaManager *manager, + PurpleMediaSessionType type, PurpleMedia *media, + const gchar *session_id, const gchar *participant) +{ +#ifdef USE_VV + GstElement *ret = NULL; + PurpleMediaElementInfo *info = NULL; + PurpleMediaElementType element_type; + + if (type & PURPLE_MEDIA_SEND_AUDIO) + info = manager->priv->audio_src; + else if (type & PURPLE_MEDIA_RECV_AUDIO) + info = manager->priv->audio_sink; + else if (type & PURPLE_MEDIA_SEND_VIDEO) + info = manager->priv->video_src; + else if (type & PURPLE_MEDIA_RECV_VIDEO) + info = manager->priv->video_sink; + + if (info == NULL) + return NULL; + + element_type = purple_media_element_info_get_element_type(info); + + if (element_type & PURPLE_MEDIA_ELEMENT_UNIQUE && + element_type & PURPLE_MEDIA_ELEMENT_SRC) { + GstElement *tee; + GstPad *pad; + GstPad *ghost; + gchar *id = purple_media_element_info_get_id(info); + + ret = gst_bin_get_by_name(GST_BIN( + purple_media_manager_get_pipeline( + manager)), id); + + if (ret == NULL) { + GstElement *bin, *fakesink; + ret = purple_media_element_info_call_create(info, + media, session_id, participant); + bin = gst_bin_new(id); + tee = gst_element_factory_make("tee", "tee"); + gst_bin_add_many(GST_BIN(bin), ret, tee, NULL); + gst_element_link(ret, tee); + + /* + * This shouldn't be necessary, but it stops it from + * giving a not-linked error upon destruction + */ + fakesink = gst_element_factory_make("fakesink", NULL); + g_object_set(fakesink, "sync", FALSE, NULL); + gst_bin_add(GST_BIN(bin), fakesink); + gst_element_link(tee, fakesink); + + ret = bin; + gst_object_ref(ret); + gst_bin_add(GST_BIN(purple_media_manager_get_pipeline( + manager)), ret); + } + g_free(id); + + tee = gst_bin_get_by_name(GST_BIN(ret), "tee"); + pad = gst_element_get_request_pad(tee, "src%d"); + gst_object_unref(tee); + ghost = gst_ghost_pad_new(NULL, pad); + gst_object_unref(pad); + g_signal_connect(GST_PAD(ghost), "unlinked", + G_CALLBACK(request_pad_unlinked_cb), NULL); + gst_pad_set_active(ghost, TRUE); + gst_element_add_pad(ret, ghost); + } else { + ret = purple_media_element_info_call_create(info, + media, session_id, participant); + } + + if (ret == NULL) + purple_debug_error("media", "Error creating source or sink\n"); + + return ret; +#else + return NULL; +#endif +} + +PurpleMediaElementInfo * +purple_media_manager_get_element_info(PurpleMediaManager *manager, + const gchar *id) +{ +#ifdef USE_VV + GList *iter; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL); + + iter = manager->priv->elements; + + for (; iter; iter = g_list_next(iter)) { + gchar *element_id = + purple_media_element_info_get_id(iter->data); + if (!strcmp(element_id, id)) { + g_free(element_id); + g_object_ref(iter->data); + return iter->data; + } + g_free(element_id); + } +#endif + + return NULL; +} + +gboolean +purple_media_manager_register_element(PurpleMediaManager *manager, + PurpleMediaElementInfo *info) +{ +#ifdef USE_VV + PurpleMediaElementInfo *info2; + gchar *id; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + g_return_val_if_fail(info != NULL, FALSE); + + id = purple_media_element_info_get_id(info); + info2 = purple_media_manager_get_element_info(manager, id); + g_free(id); + + if (info2 != NULL) { + g_object_unref(info2); + return FALSE; + } + + manager->priv->elements = + g_list_prepend(manager->priv->elements, info); + return TRUE; +#else + return FALSE; +#endif +} + +gboolean +purple_media_manager_unregister_element(PurpleMediaManager *manager, + const gchar *id) +{ +#ifdef USE_VV + PurpleMediaElementInfo *info; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + + info = purple_media_manager_get_element_info(manager, id); + + if (info == NULL) { + g_object_unref(info); + return FALSE; + } + + if (manager->priv->audio_src == info) + manager->priv->audio_src = NULL; + if (manager->priv->audio_sink == info) + manager->priv->audio_sink = NULL; + if (manager->priv->video_src == info) + manager->priv->video_src = NULL; + if (manager->priv->video_sink == info) + manager->priv->video_sink = NULL; + + manager->priv->elements = g_list_remove( + manager->priv->elements, info); + g_object_unref(info); + return TRUE; +#else + return FALSE; +#endif +} + +gboolean +purple_media_manager_set_active_element(PurpleMediaManager *manager, + PurpleMediaElementInfo *info) +{ +#ifdef USE_VV + PurpleMediaElementInfo *info2; + PurpleMediaElementType type; + gboolean ret = FALSE; + gchar *id; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + g_return_val_if_fail(info != NULL, FALSE); + + id = purple_media_element_info_get_id(info); + info2 = purple_media_manager_get_element_info(manager, id); + g_free(id); + + if (info2 == NULL) + purple_media_manager_register_element(manager, info); + else + g_object_unref(info2); + + type = purple_media_element_info_get_element_type(info); + + if (type & PURPLE_MEDIA_ELEMENT_SRC) { + if (type & PURPLE_MEDIA_ELEMENT_AUDIO) { + manager->priv->audio_src = info; + ret = TRUE; + } + if (type & PURPLE_MEDIA_ELEMENT_VIDEO) { + manager->priv->video_src = info; + ret = TRUE; + } + } + if (type & PURPLE_MEDIA_ELEMENT_SINK) { + if (type & PURPLE_MEDIA_ELEMENT_AUDIO) { + manager->priv->audio_sink = info; + ret = TRUE; + } + if (type & PURPLE_MEDIA_ELEMENT_VIDEO) { + manager->priv->video_sink = info; + ret = TRUE; + } + } + + return ret; +#else + return FALSE; +#endif +} + +PurpleMediaElementInfo * +purple_media_manager_get_active_element(PurpleMediaManager *manager, + PurpleMediaElementType type) +{ +#ifdef USE_VV + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL); + + if (type & PURPLE_MEDIA_ELEMENT_SRC) { + if (type & PURPLE_MEDIA_ELEMENT_AUDIO) + return manager->priv->audio_src; + else if (type & PURPLE_MEDIA_ELEMENT_VIDEO) + return manager->priv->video_src; + } else if (type & PURPLE_MEDIA_ELEMENT_SINK) { + if (type & PURPLE_MEDIA_ELEMENT_AUDIO) + return manager->priv->audio_sink; + else if (type & PURPLE_MEDIA_ELEMENT_VIDEO) + return manager->priv->video_sink; + } +#endif + + return NULL; +} +#endif /* USE_GSTREAMER */ + +#ifdef USE_VV +static void +window_id_cb(GstBus *bus, GstMessage *msg, PurpleMediaOutputWindow *ow) +{ + GstElement *sink; + + if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT || + !gst_structure_has_name(msg->structure, + "prepare-xwindow-id")) + return; + + sink = GST_ELEMENT(GST_MESSAGE_SRC(msg)); + while (sink != ow->sink) { + if (sink == NULL) + return; + sink = GST_ELEMENT_PARENT(sink); + } + + g_signal_handlers_disconnect_matched(bus, G_SIGNAL_MATCH_FUNC + | G_SIGNAL_MATCH_DATA, 0, 0, NULL, + window_id_cb, ow); + + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY( + GST_MESSAGE_SRC(msg)), ow->window_id); +} +#endif + +gboolean +purple_media_manager_create_output_window(PurpleMediaManager *manager, + PurpleMedia *media, const gchar *session_id, + const gchar *participant) +{ +#ifdef USE_VV + GList *iter; + + g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); + + iter = manager->priv->output_windows; + for(; iter; iter = g_list_next(iter)) { + PurpleMediaOutputWindow *ow = iter->data; + + if (ow->sink == NULL && ow->media == media && + ((participant != NULL && + ow->participant != NULL && + !strcmp(participant, ow->participant)) || + (participant == ow->participant)) && + !strcmp(session_id, ow->session_id)) { + GstBus *bus; + GstElement *queue, *colorspace; + GstElement *tee = purple_media_get_tee(media, + session_id, participant); + + if (tee == NULL) + continue; + + queue = gst_element_factory_make( + "queue", NULL); + colorspace = gst_element_factory_make( + "ffmpegcolorspace", NULL); + ow->sink = purple_media_manager_get_element( + manager, PURPLE_MEDIA_RECV_VIDEO, + ow->media, ow->session_id, + ow->participant); + + if (participant == NULL) { + /* aka this is a preview sink */ + GObjectClass *klass = + G_OBJECT_GET_CLASS(ow->sink); + if (g_object_class_find_property(klass, + "sync")) + g_object_set(G_OBJECT(ow->sink), + "sync", "FALSE", NULL); + if (g_object_class_find_property(klass, + "async")) + g_object_set(G_OBJECT(ow->sink), + "async", FALSE, NULL); + } + + gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee)), + queue, colorspace, ow->sink, NULL); + + bus = gst_pipeline_get_bus(GST_PIPELINE( + manager->priv->pipeline)); + g_signal_connect(bus, "sync-message::element", + G_CALLBACK(window_id_cb), ow); + gst_object_unref(bus); + + gst_element_set_state(ow->sink, GST_STATE_PLAYING); + gst_element_set_state(colorspace, GST_STATE_PLAYING); + gst_element_set_state(queue, GST_STATE_PLAYING); + gst_element_link(colorspace, ow->sink); + gst_element_link(queue, colorspace); + gst_element_link(tee, queue); + } + } + return TRUE; +#else + return FALSE; +#endif +} + +gulong +purple_media_manager_set_output_window(PurpleMediaManager *manager, + PurpleMedia *media, const gchar *session_id, + const gchar *participant, gulong window_id) +{ +#ifdef USE_VV + PurpleMediaOutputWindow *output_window; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); + + output_window = g_new0(PurpleMediaOutputWindow, 1); + output_window->id = manager->priv->next_output_window_id++; + output_window->media = media; + output_window->session_id = g_strdup(session_id); + output_window->participant = g_strdup(participant); + output_window->window_id = window_id; + + manager->priv->output_windows = g_list_prepend( + manager->priv->output_windows, output_window); + + if (purple_media_get_tee(media, session_id, participant) != NULL) + purple_media_manager_create_output_window(manager, + media, session_id, participant); + + return output_window->id; +#else + return 0; +#endif +} + +gboolean +purple_media_manager_remove_output_window(PurpleMediaManager *manager, + gulong output_window_id) +{ +#ifdef USE_VV + PurpleMediaOutputWindow *output_window = NULL; + GList *iter; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + + iter = manager->priv->output_windows; + for (; iter; iter = g_list_next(iter)) { + PurpleMediaOutputWindow *ow = iter->data; + if (ow->id == output_window_id) { + manager->priv->output_windows = g_list_delete_link( + manager->priv->output_windows, iter); + output_window = ow; + break; + } + } + + if (output_window == NULL) + return FALSE; + + if (output_window->sink != NULL) { + GstPad *pad = gst_element_get_static_pad( + output_window->sink, "sink"); + GstPad *peer = gst_pad_get_peer(pad); + GstElement *colorspace = GST_ELEMENT_PARENT(peer), *queue; + gst_object_unref(pad); + gst_object_unref(peer); + pad = gst_element_get_static_pad(colorspace, "sink"); + peer = gst_pad_get_peer(pad); + queue = GST_ELEMENT_PARENT(peer); + gst_object_unref(pad); + gst_object_unref(peer); + pad = gst_element_get_static_pad(queue, "sink"); + peer = gst_pad_get_peer(pad); + gst_object_unref(pad); + if (peer != NULL) + gst_element_release_request_pad(GST_ELEMENT_PARENT(peer), peer); + gst_element_set_locked_state(queue, TRUE); + gst_element_set_state(queue, GST_STATE_NULL); + gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(queue)), queue); + gst_element_set_locked_state(colorspace, TRUE); + gst_element_set_state(colorspace, GST_STATE_NULL); + gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(colorspace)), colorspace); + gst_element_set_locked_state(output_window->sink, TRUE); + gst_element_set_state(output_window->sink, GST_STATE_NULL); + gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(output_window->sink)), + output_window->sink); + } + + g_free(output_window->session_id); + g_free(output_window->participant); + g_free(output_window); + + return TRUE; +#else + return FALSE; +#endif +} + +void +purple_media_manager_remove_output_windows(PurpleMediaManager *manager, + PurpleMedia *media, const gchar *session_id, + const gchar *participant) +{ +#ifdef USE_VV + GList *iter; + + g_return_if_fail(PURPLE_IS_MEDIA(media)); + + iter = manager->priv->output_windows; + + for (; iter;) { + PurpleMediaOutputWindow *ow = iter->data; + iter = g_list_next(iter); + + if (media == ow->media && + ((session_id != NULL && ow->session_id != NULL && + !strcmp(session_id, ow->session_id)) || + (session_id == ow->session_id)) && + ((participant != NULL && ow->participant != NULL && + !strcmp(participant, ow->participant)) || + (participant == ow->participant))) + purple_media_manager_remove_output_window( + manager, ow->id); + } +#endif +} + +void +purple_media_manager_set_ui_caps(PurpleMediaManager *manager, + PurpleMediaCaps caps) +{ +#ifdef USE_VV + g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager)); + manager->priv->ui_caps = caps; +#endif +} + +PurpleMediaCaps +purple_media_manager_get_ui_caps(PurpleMediaManager *manager) +{ +#ifdef USE_VV + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), + PURPLE_MEDIA_CAPS_NONE); + return manager->priv->ui_caps; +#else + return PURPLE_MEDIA_CAPS_NONE; +#endif +} + +#ifdef USE_GSTREAMER + +/* + * PurpleMediaElementType + */ + +GType +purple_media_element_type_get_type() +{ + static GType type = 0; + if (type == 0) { + static const GFlagsValue values[] = { + { PURPLE_MEDIA_ELEMENT_NONE, + "PURPLE_MEDIA_ELEMENT_NONE", "none" }, + { PURPLE_MEDIA_ELEMENT_AUDIO, + "PURPLE_MEDIA_ELEMENT_AUDIO", "audio" }, + { PURPLE_MEDIA_ELEMENT_VIDEO, + "PURPLE_MEDIA_ELEMENT_VIDEO", "video" }, + { PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO, + "PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO", + "audio-video" }, + { PURPLE_MEDIA_ELEMENT_NO_SRCS, + "PURPLE_MEDIA_ELEMENT_NO_SRCS", "no-srcs" }, + { PURPLE_MEDIA_ELEMENT_ONE_SRC, + "PURPLE_MEDIA_ELEMENT_ONE_SRC", "one-src" }, + { PURPLE_MEDIA_ELEMENT_MULTI_SRC, + "PURPLE_MEDIA_ELEMENT_MULTI_SRC", + "multi-src" }, + { PURPLE_MEDIA_ELEMENT_REQUEST_SRC, + "PURPLE_MEDIA_ELEMENT_REQUEST_SRC", + "request-src" }, + { PURPLE_MEDIA_ELEMENT_NO_SINKS, + "PURPLE_MEDIA_ELEMENT_NO_SINKS", "no-sinks" }, + { PURPLE_MEDIA_ELEMENT_ONE_SINK, + "PURPLE_MEDIA_ELEMENT_ONE_SINK", "one-sink" }, + { PURPLE_MEDIA_ELEMENT_MULTI_SINK, + "PURPLE_MEDIA_ELEMENT_MULTI_SINK", + "multi-sink" }, + { PURPLE_MEDIA_ELEMENT_REQUEST_SINK, + "PURPLE_MEDIA_ELEMENT_REQUEST_SINK", + "request-sink" }, + { PURPLE_MEDIA_ELEMENT_UNIQUE, + "PURPLE_MEDIA_ELEMENT_UNIQUE", "unique" }, + { PURPLE_MEDIA_ELEMENT_SRC, + "PURPLE_MEDIA_ELEMENT_SRC", "src" }, + { PURPLE_MEDIA_ELEMENT_SINK, + "PURPLE_MEDIA_ELEMENT_SINK", "sink" }, + { 0, NULL, NULL } + }; + type = g_flags_register_static( + "PurpleMediaElementType", values); + } + return type; +} + +/* + * PurpleMediaElementInfo + */ + +struct _PurpleMediaElementInfoClass +{ + GObjectClass parent_class; +}; + +struct _PurpleMediaElementInfo +{ + GObject parent; +}; + +#ifdef USE_VV +struct _PurpleMediaElementInfoPrivate +{ + gchar *id; + gchar *name; + PurpleMediaElementType type; + PurpleMediaElementCreateCallback create; +}; + +enum { + PROP_0, + PROP_ID, + PROP_NAME, + PROP_TYPE, + PROP_CREATE_CB, +}; + +static void +purple_media_element_info_init(PurpleMediaElementInfo *info) +{ + PurpleMediaElementInfoPrivate *priv = + PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(info); + priv->id = NULL; + priv->name = NULL; + priv->type = PURPLE_MEDIA_ELEMENT_NONE; + priv->create = NULL; +} + +static void +purple_media_element_info_finalize(GObject *info) +{ + PurpleMediaElementInfoPrivate *priv = + PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(info); + g_free(priv->id); + g_free(priv->name); +} + +static void +purple_media_element_info_set_property (GObject *object, guint prop_id, + const GValue *value, GParamSpec *pspec) +{ + PurpleMediaElementInfoPrivate *priv; + g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object)); + + priv = PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_ID: + g_free(priv->id); + priv->id = g_value_dup_string(value); + break; + case PROP_NAME: + g_free(priv->name); + priv->name = g_value_dup_string(value); + break; + case PROP_TYPE: { + priv->type = g_value_get_flags(value); + break; + } + case PROP_CREATE_CB: + priv->create = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( + object, prop_id, pspec); + break; + } +} + +static void +purple_media_element_info_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + PurpleMediaElementInfoPrivate *priv; + g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object)); + + priv = PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_ID: + g_value_set_string(value, priv->id); + break; + case PROP_NAME: + g_value_set_string(value, priv->name); + break; + case PROP_TYPE: + g_value_set_flags(value, priv->type); + break; + case PROP_CREATE_CB: + g_value_set_pointer(value, priv->create); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID( + object, prop_id, pspec); + break; + } +} + +static void +purple_media_element_info_class_init(PurpleMediaElementInfoClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass*)klass; + + gobject_class->finalize = purple_media_element_info_finalize; + gobject_class->set_property = purple_media_element_info_set_property; + gobject_class->get_property = purple_media_element_info_get_property; + + g_object_class_install_property(gobject_class, PROP_ID, + g_param_spec_string("id", + "ID", + "The unique identifier of the element.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_NAME, + g_param_spec_string("name", + "Name", + "The friendly/display name of this element.", + NULL, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_TYPE, + g_param_spec_flags("type", + "Element Type", + "The type of element this is.", + PURPLE_TYPE_MEDIA_ELEMENT_TYPE, + PURPLE_MEDIA_ELEMENT_NONE, + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_object_class_install_property(gobject_class, PROP_CREATE_CB, + g_param_spec_pointer("create-cb", + "Create Callback", + "The function called to create this element.", + G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE)); + + g_type_class_add_private(klass, sizeof(PurpleMediaElementInfoPrivate)); +} + +G_DEFINE_TYPE(PurpleMediaElementInfo, + purple_media_element_info, G_TYPE_OBJECT); +#else +GType +purple_media_element_info_get_type() +{ + return G_TYPE_NONE; +} +#endif + +gchar * +purple_media_element_info_get_id(PurpleMediaElementInfo *info) +{ +#ifdef USE_VV + gchar *id; + g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info), NULL); + g_object_get(info, "id", &id, NULL); + return id; +#else + return NULL; +#endif +} + +gchar * +purple_media_element_info_get_name(PurpleMediaElementInfo *info) +{ +#ifdef USE_VV + gchar *name; + g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info), NULL); + g_object_get(info, "name", &name, NULL); + return name; +#else + return NULL; +#endif +} + +PurpleMediaElementType +purple_media_element_info_get_element_type(PurpleMediaElementInfo *info) +{ +#ifdef USE_VV + PurpleMediaElementType type; + g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info), + PURPLE_MEDIA_ELEMENT_NONE); + g_object_get(info, "type", &type, NULL); + return type; +#else + return PURPLE_MEDIA_ELEMENT_NONE; +#endif +} + +GstElement * +purple_media_element_info_call_create(PurpleMediaElementInfo *info, + PurpleMedia *media, const gchar *session_id, + const gchar *participant) +{ +#ifdef USE_VV + PurpleMediaElementCreateCallback create; + g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info), NULL); + g_object_get(info, "create-cb", &create, NULL); + if (create) + return create(media, session_id, participant); +#endif + return NULL; +} + +#endif /* USE_GSTREAMER */ +