Write this item more similarly to other items
[pidgin-git.git] / libpurple / mediamanager.c
blob48d034c05f8428c40f56b8e9d8a73dfe36f478d7
1 /**
2 * @file mediamanager.c Media Manager API
3 * @ingroup core
4 */
6 /* purple
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "internal.h"
29 #include "account.h"
30 #include "debug.h"
31 #include "media.h"
32 #include "mediamanager.h"
34 #ifdef USE_GSTREAMER
35 #include "marshallers.h"
36 #include "media-gst.h"
37 #endif
39 #ifdef USE_VV
40 #include <media/backend-fs2.h>
42 #include <gst/farsight/fs-element-added-notifier.h>
43 #include <gst/interfaces/xoverlay.h>
45 /** @copydoc _PurpleMediaManagerPrivate */
46 typedef struct _PurpleMediaManagerPrivate PurpleMediaManagerPrivate;
47 /** @copydoc _PurpleMediaOutputWindow */
48 typedef struct _PurpleMediaOutputWindow PurpleMediaOutputWindow;
49 /** @copydoc _PurpleMediaManagerPrivate */
50 typedef struct _PurpleMediaElementInfoPrivate PurpleMediaElementInfoPrivate;
52 /** The media manager class. */
53 struct _PurpleMediaManagerClass
55 GObjectClass parent_class; /**< The parent class. */
58 /** The media manager's data. */
59 struct _PurpleMediaManager
61 GObject parent; /**< The parent of this manager. */
62 PurpleMediaManagerPrivate *priv; /**< Private data for the manager. */
65 struct _PurpleMediaOutputWindow
67 gulong id;
68 PurpleMedia *media;
69 gchar *session_id;
70 gchar *participant;
71 gulong window_id;
72 GstElement *sink;
75 struct _PurpleMediaManagerPrivate
77 GstElement *pipeline;
78 PurpleMediaCaps ui_caps;
79 GList *medias;
80 GList *elements;
81 GList *output_windows;
82 gulong next_output_window_id;
83 GType backend_type;
84 GstCaps *video_caps;
86 PurpleMediaElementInfo *video_src;
87 PurpleMediaElementInfo *video_sink;
88 PurpleMediaElementInfo *audio_src;
89 PurpleMediaElementInfo *audio_sink;
92 #define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate))
93 #define PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfoPrivate))
95 static void purple_media_manager_class_init (PurpleMediaManagerClass *klass);
96 static void purple_media_manager_init (PurpleMediaManager *media);
97 static void purple_media_manager_finalize (GObject *object);
99 static GObjectClass *parent_class = NULL;
103 enum {
104 INIT_MEDIA,
105 UI_CAPS_CHANGED,
106 LAST_SIGNAL
108 static guint purple_media_manager_signals[LAST_SIGNAL] = {0};
109 #endif
111 GType
112 purple_media_manager_get_type()
114 #ifdef USE_VV
115 static GType type = 0;
117 if (type == 0) {
118 static const GTypeInfo info = {
119 sizeof(PurpleMediaManagerClass),
120 NULL,
121 NULL,
122 (GClassInitFunc) purple_media_manager_class_init,
123 NULL,
124 NULL,
125 sizeof(PurpleMediaManager),
127 (GInstanceInitFunc) purple_media_manager_init,
128 NULL
130 type = g_type_register_static(G_TYPE_OBJECT, "PurpleMediaManager", &info, 0);
132 return type;
133 #else
134 return G_TYPE_NONE;
135 #endif
138 #ifdef USE_VV
139 static void
140 purple_media_manager_class_init (PurpleMediaManagerClass *klass)
142 GObjectClass *gobject_class = (GObjectClass*)klass;
143 parent_class = g_type_class_peek_parent(klass);
145 gobject_class->finalize = purple_media_manager_finalize;
147 purple_media_manager_signals[INIT_MEDIA] = g_signal_new ("init-media",
148 G_TYPE_FROM_CLASS (klass),
149 G_SIGNAL_RUN_LAST,
150 0, NULL, NULL,
151 purple_smarshal_BOOLEAN__OBJECT_POINTER_STRING,
152 G_TYPE_BOOLEAN, 3, PURPLE_TYPE_MEDIA,
153 G_TYPE_POINTER, G_TYPE_STRING);
155 purple_media_manager_signals[UI_CAPS_CHANGED] = g_signal_new ("ui-caps-changed",
156 G_TYPE_FROM_CLASS (klass),
157 G_SIGNAL_RUN_LAST,
158 0, NULL, NULL,
159 purple_smarshal_VOID__FLAGS_FLAGS,
160 G_TYPE_NONE, 2, PURPLE_MEDIA_TYPE_CAPS,
161 PURPLE_MEDIA_TYPE_CAPS);
163 g_type_class_add_private(klass, sizeof(PurpleMediaManagerPrivate));
166 static void
167 purple_media_manager_init (PurpleMediaManager *media)
169 media->priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media);
170 media->priv->medias = NULL;
171 media->priv->next_output_window_id = 1;
172 #ifdef USE_VV
173 media->priv->backend_type = PURPLE_TYPE_MEDIA_BACKEND_FS2;
174 #endif
176 purple_prefs_add_none("/purple/media");
177 purple_prefs_add_none("/purple/media/audio");
178 purple_prefs_add_int("/purple/media/audio/silence_threshold", 5);
179 purple_prefs_add_none("/purple/media/audio/volume");
180 purple_prefs_add_int("/purple/media/audio/volume/input", 10);
181 purple_prefs_add_int("/purple/media/audio/volume/output", 10);
184 static void
185 purple_media_manager_finalize (GObject *media)
187 PurpleMediaManagerPrivate *priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media);
188 for (; priv->medias; priv->medias =
189 g_list_delete_link(priv->medias, priv->medias)) {
190 g_object_unref(priv->medias->data);
192 for (; priv->elements; priv->elements =
193 g_list_delete_link(priv->elements, priv->elements)) {
194 g_object_unref(priv->elements->data);
196 if (priv->video_caps)
197 gst_caps_unref(priv->video_caps);
198 parent_class->finalize(media);
200 #endif
202 PurpleMediaManager *
203 purple_media_manager_get()
205 #ifdef USE_VV
206 static PurpleMediaManager *manager = NULL;
208 if (manager == NULL)
209 manager = PURPLE_MEDIA_MANAGER(g_object_new(purple_media_manager_get_type(), NULL));
210 return manager;
211 #else
212 return NULL;
213 #endif
216 #ifdef USE_VV
217 static gboolean
218 pipeline_bus_call(GstBus *bus, GstMessage *msg, PurpleMediaManager *manager)
220 switch(GST_MESSAGE_TYPE(msg)) {
221 case GST_MESSAGE_EOS:
222 purple_debug_info("mediamanager", "End of Stream\n");
223 break;
224 case GST_MESSAGE_ERROR: {
225 gchar *debug = NULL;
226 GError *err = NULL;
228 gst_message_parse_error(msg, &err, &debug);
230 purple_debug_error("mediamanager",
231 "gst pipeline error: %s\n",
232 err->message);
233 g_error_free(err);
235 if (debug) {
236 purple_debug_error("mediamanager",
237 "Debug details: %s\n", debug);
238 g_free (debug);
240 break;
242 default:
243 break;
245 return TRUE;
247 #endif
249 #ifdef USE_GSTREAMER
250 GstElement *
251 purple_media_manager_get_pipeline(PurpleMediaManager *manager)
253 #ifdef USE_VV
254 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
256 if (manager->priv->pipeline == NULL) {
257 FsElementAddedNotifier *notifier;
258 gchar *filename;
259 GError *err = NULL;
260 GKeyFile *keyfile;
261 GstBus *bus;
262 manager->priv->pipeline = gst_pipeline_new(NULL);
264 bus = gst_pipeline_get_bus(
265 GST_PIPELINE(manager->priv->pipeline));
266 gst_bus_add_signal_watch(GST_BUS(bus));
267 g_signal_connect(G_OBJECT(bus), "message",
268 G_CALLBACK(pipeline_bus_call), manager);
269 gst_bus_set_sync_handler(bus,
270 gst_bus_sync_signal_handler, NULL);
271 gst_object_unref(bus);
273 filename = g_build_filename(purple_user_dir(),
274 "fs-element.conf", NULL);
275 keyfile = g_key_file_new();
276 if (!g_key_file_load_from_file(keyfile, filename,
277 G_KEY_FILE_NONE, &err)) {
278 if (err->code == 4)
279 purple_debug_info("mediamanager",
280 "Couldn't read "
281 "fs-element.conf: %s\n",
282 err->message);
283 else
284 purple_debug_error("mediamanager",
285 "Error reading "
286 "fs-element.conf: %s\n",
287 err->message);
288 g_error_free(err);
290 g_free(filename);
292 /* Hack to make alsasrc stop messing up audio timestamps */
293 if (!g_key_file_has_key(keyfile,
294 "alsasrc", "slave-method", NULL)) {
295 g_key_file_set_integer(keyfile,
296 "alsasrc", "slave-method", 2);
299 notifier = fs_element_added_notifier_new();
300 fs_element_added_notifier_add(notifier,
301 GST_BIN(manager->priv->pipeline));
302 fs_element_added_notifier_set_properties_from_keyfile(
303 notifier, keyfile);
305 gst_element_set_state(manager->priv->pipeline,
306 GST_STATE_PLAYING);
309 return manager->priv->pipeline;
310 #else
311 return NULL;
312 #endif
314 #endif /* USE_GSTREAMER */
316 PurpleMedia *
317 purple_media_manager_create_media(PurpleMediaManager *manager,
318 PurpleAccount *account,
319 const char *conference_type,
320 const char *remote_user,
321 gboolean initiator)
323 #ifdef USE_VV
324 PurpleMedia *media;
325 gboolean signal_ret;
327 media = PURPLE_MEDIA(g_object_new(purple_media_get_type(),
328 "manager", manager,
329 "account", account,
330 "conference-type", conference_type,
331 "initiator", initiator,
332 NULL));
334 g_signal_emit(manager, purple_media_manager_signals[INIT_MEDIA], 0,
335 media, account, remote_user, &signal_ret);
337 if (signal_ret == FALSE) {
338 g_object_unref(media);
339 return NULL;
342 manager->priv->medias = g_list_append(manager->priv->medias, media);
343 return media;
344 #else
345 return NULL;
346 #endif
349 GList *
350 purple_media_manager_get_media(PurpleMediaManager *manager)
352 #ifdef USE_VV
353 return manager->priv->medias;
354 #else
355 return NULL;
356 #endif
359 GList *
360 purple_media_manager_get_media_by_account(PurpleMediaManager *manager,
361 PurpleAccount *account)
363 #ifdef USE_VV
364 GList *media = NULL;
365 GList *iter;
367 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
369 iter = manager->priv->medias;
370 for (; iter; iter = g_list_next(iter)) {
371 if (purple_media_get_account(iter->data) == account) {
372 media = g_list_prepend(media, iter->data);
376 return media;
377 #else
378 return NULL;
379 #endif
382 void
383 purple_media_manager_remove_media(PurpleMediaManager *manager,
384 PurpleMedia *media)
386 #ifdef USE_VV
387 GList *list = g_list_find(manager->priv->medias, media);
388 if (list)
389 manager->priv->medias =
390 g_list_delete_link(manager->priv->medias, list);
391 #endif
394 #ifdef USE_VV
395 static void
396 request_pad_unlinked_cb(GstPad *pad, GstPad *peer, gpointer user_data)
398 GstElement *parent = GST_ELEMENT_PARENT(pad);
399 GstIterator *iter;
400 GstPad *remaining_pad;
401 GstIteratorResult result;
403 gst_element_release_request_pad(GST_ELEMENT_PARENT(pad), pad);
405 iter = gst_element_iterate_src_pads(parent);
407 result = gst_iterator_next(iter, (gpointer)&remaining_pad);
409 if (result == GST_ITERATOR_DONE) {
410 gst_element_set_locked_state(parent, TRUE);
411 gst_element_set_state(parent, GST_STATE_NULL);
412 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(parent)), parent);
413 } else if (result == GST_ITERATOR_OK) {
414 gst_object_unref(remaining_pad);
417 gst_iterator_free(iter);
419 #endif
421 #ifdef USE_GSTREAMER
423 void
424 purple_media_manager_set_video_caps(PurpleMediaManager *manager, GstCaps *caps)
426 #ifdef USE_VV
427 if (manager->priv->video_caps)
428 gst_caps_unref(manager->priv->video_caps);
430 manager->priv->video_caps = caps;
432 if (manager->priv->pipeline && manager->priv->video_src) {
433 gchar *id = purple_media_element_info_get_id(manager->priv->video_src);
434 GstElement *src = gst_bin_get_by_name(GST_BIN(manager->priv->pipeline), id);
436 if (src) {
437 GstElement *capsfilter = gst_bin_get_by_name(GST_BIN(src), "prpl_video_caps");
438 g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL);
441 g_free(id);
443 #endif
446 GstCaps *
447 purple_media_manager_get_video_caps(PurpleMediaManager *manager)
449 #ifdef USE_VV
450 if (manager->priv->video_caps == NULL)
451 manager->priv->video_caps = gst_caps_from_string("video/x-raw-yuv,"
452 "width=[250,352], height=[200,288], framerate=[1/1,20/1]");
453 return manager->priv->video_caps;
454 #else
455 return NULL;
456 #endif
459 GstElement *
460 purple_media_manager_get_element(PurpleMediaManager *manager,
461 PurpleMediaSessionType type, PurpleMedia *media,
462 const gchar *session_id, const gchar *participant)
464 #ifdef USE_VV
465 GstElement *ret = NULL;
466 PurpleMediaElementInfo *info = NULL;
467 PurpleMediaElementType element_type;
469 if (type & PURPLE_MEDIA_SEND_AUDIO)
470 info = manager->priv->audio_src;
471 else if (type & PURPLE_MEDIA_RECV_AUDIO)
472 info = manager->priv->audio_sink;
473 else if (type & PURPLE_MEDIA_SEND_VIDEO)
474 info = manager->priv->video_src;
475 else if (type & PURPLE_MEDIA_RECV_VIDEO)
476 info = manager->priv->video_sink;
478 if (info == NULL)
479 return NULL;
481 element_type = purple_media_element_info_get_element_type(info);
483 if (element_type & PURPLE_MEDIA_ELEMENT_UNIQUE &&
484 element_type & PURPLE_MEDIA_ELEMENT_SRC) {
485 GstElement *tee;
486 GstPad *pad;
487 GstPad *ghost;
488 gchar *id = purple_media_element_info_get_id(info);
490 ret = gst_bin_get_by_name(GST_BIN(
491 purple_media_manager_get_pipeline(
492 manager)), id);
494 if (ret == NULL) {
495 GstElement *bin, *fakesink;
496 ret = purple_media_element_info_call_create(info,
497 media, session_id, participant);
498 bin = gst_bin_new(id);
499 tee = gst_element_factory_make("tee", "tee");
500 gst_bin_add_many(GST_BIN(bin), ret, tee, NULL);
502 if (type & PURPLE_MEDIA_SEND_VIDEO) {
503 GstElement *videoscale;
504 GstElement *capsfilter;
506 videoscale = gst_element_factory_make("videoscale", NULL);
507 capsfilter = gst_element_factory_make("capsfilter", "prpl_video_caps");
509 g_object_set(G_OBJECT(capsfilter),
510 "caps", purple_media_manager_get_video_caps(manager), NULL);
512 gst_bin_add_many(GST_BIN(bin), videoscale, capsfilter, NULL);
513 gst_element_link_many(ret, videoscale, capsfilter, tee, NULL);
514 } else
515 gst_element_link(ret, tee);
518 * This shouldn't be necessary, but it stops it from
519 * giving a not-linked error upon destruction
521 fakesink = gst_element_factory_make("fakesink", NULL);
522 g_object_set(fakesink, "sync", FALSE, NULL);
523 gst_bin_add(GST_BIN(bin), fakesink);
524 gst_element_link(tee, fakesink);
526 ret = bin;
527 gst_object_ref(ret);
528 gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(
529 manager)), ret);
531 g_free(id);
533 tee = gst_bin_get_by_name(GST_BIN(ret), "tee");
534 pad = gst_element_get_request_pad(tee, "src%d");
535 gst_object_unref(tee);
536 ghost = gst_ghost_pad_new(NULL, pad);
537 gst_object_unref(pad);
538 g_signal_connect(GST_PAD(ghost), "unlinked",
539 G_CALLBACK(request_pad_unlinked_cb), NULL);
540 gst_pad_set_active(ghost, TRUE);
541 gst_element_add_pad(ret, ghost);
542 } else {
543 ret = purple_media_element_info_call_create(info,
544 media, session_id, participant);
547 if (ret == NULL)
548 purple_debug_error("media", "Error creating source or sink\n");
550 return ret;
551 #else
552 return NULL;
553 #endif
556 PurpleMediaElementInfo *
557 purple_media_manager_get_element_info(PurpleMediaManager *manager,
558 const gchar *id)
560 #ifdef USE_VV
561 GList *iter;
563 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
565 iter = manager->priv->elements;
567 for (; iter; iter = g_list_next(iter)) {
568 gchar *element_id =
569 purple_media_element_info_get_id(iter->data);
570 if (!strcmp(element_id, id)) {
571 g_free(element_id);
572 g_object_ref(iter->data);
573 return iter->data;
575 g_free(element_id);
577 #endif
579 return NULL;
582 gboolean
583 purple_media_manager_register_element(PurpleMediaManager *manager,
584 PurpleMediaElementInfo *info)
586 #ifdef USE_VV
587 PurpleMediaElementInfo *info2;
588 gchar *id;
590 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
591 g_return_val_if_fail(info != NULL, FALSE);
593 id = purple_media_element_info_get_id(info);
594 info2 = purple_media_manager_get_element_info(manager, id);
595 g_free(id);
597 if (info2 != NULL) {
598 g_object_unref(info2);
599 return FALSE;
602 manager->priv->elements =
603 g_list_prepend(manager->priv->elements, info);
604 return TRUE;
605 #else
606 return FALSE;
607 #endif
610 gboolean
611 purple_media_manager_unregister_element(PurpleMediaManager *manager,
612 const gchar *id)
614 #ifdef USE_VV
615 PurpleMediaElementInfo *info;
617 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
619 info = purple_media_manager_get_element_info(manager, id);
621 if (info == NULL) {
622 g_object_unref(info);
623 return FALSE;
626 if (manager->priv->audio_src == info)
627 manager->priv->audio_src = NULL;
628 if (manager->priv->audio_sink == info)
629 manager->priv->audio_sink = NULL;
630 if (manager->priv->video_src == info)
631 manager->priv->video_src = NULL;
632 if (manager->priv->video_sink == info)
633 manager->priv->video_sink = NULL;
635 manager->priv->elements = g_list_remove(
636 manager->priv->elements, info);
637 g_object_unref(info);
638 return TRUE;
639 #else
640 return FALSE;
641 #endif
644 gboolean
645 purple_media_manager_set_active_element(PurpleMediaManager *manager,
646 PurpleMediaElementInfo *info)
648 #ifdef USE_VV
649 PurpleMediaElementInfo *info2;
650 PurpleMediaElementType type;
651 gboolean ret = FALSE;
652 gchar *id;
654 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
655 g_return_val_if_fail(info != NULL, FALSE);
657 id = purple_media_element_info_get_id(info);
658 info2 = purple_media_manager_get_element_info(manager, id);
659 g_free(id);
661 if (info2 == NULL)
662 purple_media_manager_register_element(manager, info);
663 else
664 g_object_unref(info2);
666 type = purple_media_element_info_get_element_type(info);
668 if (type & PURPLE_MEDIA_ELEMENT_SRC) {
669 if (type & PURPLE_MEDIA_ELEMENT_AUDIO) {
670 manager->priv->audio_src = info;
671 ret = TRUE;
673 if (type & PURPLE_MEDIA_ELEMENT_VIDEO) {
674 manager->priv->video_src = info;
675 ret = TRUE;
678 if (type & PURPLE_MEDIA_ELEMENT_SINK) {
679 if (type & PURPLE_MEDIA_ELEMENT_AUDIO) {
680 manager->priv->audio_sink = info;
681 ret = TRUE;
683 if (type & PURPLE_MEDIA_ELEMENT_VIDEO) {
684 manager->priv->video_sink = info;
685 ret = TRUE;
689 return ret;
690 #else
691 return FALSE;
692 #endif
695 PurpleMediaElementInfo *
696 purple_media_manager_get_active_element(PurpleMediaManager *manager,
697 PurpleMediaElementType type)
699 #ifdef USE_VV
700 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
702 if (type & PURPLE_MEDIA_ELEMENT_SRC) {
703 if (type & PURPLE_MEDIA_ELEMENT_AUDIO)
704 return manager->priv->audio_src;
705 else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
706 return manager->priv->video_src;
707 } else if (type & PURPLE_MEDIA_ELEMENT_SINK) {
708 if (type & PURPLE_MEDIA_ELEMENT_AUDIO)
709 return manager->priv->audio_sink;
710 else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
711 return manager->priv->video_sink;
713 #endif
715 return NULL;
717 #endif /* USE_GSTREAMER */
719 #ifdef USE_VV
720 static void
721 window_id_cb(GstBus *bus, GstMessage *msg, PurpleMediaOutputWindow *ow)
723 GstElement *sink;
725 if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT ||
726 !gst_structure_has_name(msg->structure,
727 "prepare-xwindow-id"))
728 return;
730 sink = GST_ELEMENT(GST_MESSAGE_SRC(msg));
731 while (sink != ow->sink) {
732 if (sink == NULL)
733 return;
734 sink = GST_ELEMENT_PARENT(sink);
737 g_signal_handlers_disconnect_matched(bus, G_SIGNAL_MATCH_FUNC
738 | G_SIGNAL_MATCH_DATA, 0, 0, NULL,
739 window_id_cb, ow);
741 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(
742 GST_MESSAGE_SRC(msg)), ow->window_id);
744 #endif
746 gboolean
747 purple_media_manager_create_output_window(PurpleMediaManager *manager,
748 PurpleMedia *media, const gchar *session_id,
749 const gchar *participant)
751 #ifdef USE_VV
752 GList *iter;
754 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
756 iter = manager->priv->output_windows;
757 for(; iter; iter = g_list_next(iter)) {
758 PurpleMediaOutputWindow *ow = iter->data;
760 if (ow->sink == NULL && ow->media == media &&
761 ((participant != NULL &&
762 ow->participant != NULL &&
763 !strcmp(participant, ow->participant)) ||
764 (participant == ow->participant)) &&
765 !strcmp(session_id, ow->session_id)) {
766 GstBus *bus;
767 GstElement *queue, *colorspace;
768 GstElement *tee = purple_media_get_tee(media,
769 session_id, participant);
771 if (tee == NULL)
772 continue;
774 queue = gst_element_factory_make(
775 "queue", NULL);
776 colorspace = gst_element_factory_make(
777 "ffmpegcolorspace", NULL);
778 ow->sink = purple_media_manager_get_element(
779 manager, PURPLE_MEDIA_RECV_VIDEO,
780 ow->media, ow->session_id,
781 ow->participant);
783 if (participant == NULL) {
784 /* aka this is a preview sink */
785 GObjectClass *klass =
786 G_OBJECT_GET_CLASS(ow->sink);
787 if (g_object_class_find_property(klass,
788 "sync"))
789 g_object_set(G_OBJECT(ow->sink),
790 "sync", "FALSE", NULL);
791 if (g_object_class_find_property(klass,
792 "async"))
793 g_object_set(G_OBJECT(ow->sink),
794 "async", FALSE, NULL);
797 gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee)),
798 queue, colorspace, ow->sink, NULL);
800 bus = gst_pipeline_get_bus(GST_PIPELINE(
801 manager->priv->pipeline));
802 g_signal_connect(bus, "sync-message::element",
803 G_CALLBACK(window_id_cb), ow);
804 gst_object_unref(bus);
806 gst_element_set_state(ow->sink, GST_STATE_PLAYING);
807 gst_element_set_state(colorspace, GST_STATE_PLAYING);
808 gst_element_set_state(queue, GST_STATE_PLAYING);
809 gst_element_link(colorspace, ow->sink);
810 gst_element_link(queue, colorspace);
811 gst_element_link(tee, queue);
814 return TRUE;
815 #else
816 return FALSE;
817 #endif
820 gulong
821 purple_media_manager_set_output_window(PurpleMediaManager *manager,
822 PurpleMedia *media, const gchar *session_id,
823 const gchar *participant, gulong window_id)
825 #ifdef USE_VV
826 PurpleMediaOutputWindow *output_window;
828 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
829 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
831 output_window = g_new0(PurpleMediaOutputWindow, 1);
832 output_window->id = manager->priv->next_output_window_id++;
833 output_window->media = media;
834 output_window->session_id = g_strdup(session_id);
835 output_window->participant = g_strdup(participant);
836 output_window->window_id = window_id;
838 manager->priv->output_windows = g_list_prepend(
839 manager->priv->output_windows, output_window);
841 if (purple_media_get_tee(media, session_id, participant) != NULL)
842 purple_media_manager_create_output_window(manager,
843 media, session_id, participant);
845 return output_window->id;
846 #else
847 return 0;
848 #endif
851 gboolean
852 purple_media_manager_remove_output_window(PurpleMediaManager *manager,
853 gulong output_window_id)
855 #ifdef USE_VV
856 PurpleMediaOutputWindow *output_window = NULL;
857 GList *iter;
859 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
861 iter = manager->priv->output_windows;
862 for (; iter; iter = g_list_next(iter)) {
863 PurpleMediaOutputWindow *ow = iter->data;
864 if (ow->id == output_window_id) {
865 manager->priv->output_windows = g_list_delete_link(
866 manager->priv->output_windows, iter);
867 output_window = ow;
868 break;
872 if (output_window == NULL)
873 return FALSE;
875 if (output_window->sink != NULL) {
876 GstPad *pad = gst_element_get_static_pad(
877 output_window->sink, "sink");
878 GstPad *peer = gst_pad_get_peer(pad);
879 GstElement *colorspace = GST_ELEMENT_PARENT(peer), *queue;
880 gst_object_unref(pad);
881 gst_object_unref(peer);
882 pad = gst_element_get_static_pad(colorspace, "sink");
883 peer = gst_pad_get_peer(pad);
884 queue = GST_ELEMENT_PARENT(peer);
885 gst_object_unref(pad);
886 gst_object_unref(peer);
887 pad = gst_element_get_static_pad(queue, "sink");
888 peer = gst_pad_get_peer(pad);
889 gst_object_unref(pad);
890 if (peer != NULL)
891 gst_element_release_request_pad(GST_ELEMENT_PARENT(peer), peer);
892 gst_element_set_locked_state(queue, TRUE);
893 gst_element_set_state(queue, GST_STATE_NULL);
894 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(queue)), queue);
895 gst_element_set_locked_state(colorspace, TRUE);
896 gst_element_set_state(colorspace, GST_STATE_NULL);
897 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(colorspace)), colorspace);
898 gst_element_set_locked_state(output_window->sink, TRUE);
899 gst_element_set_state(output_window->sink, GST_STATE_NULL);
900 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(output_window->sink)),
901 output_window->sink);
904 g_free(output_window->session_id);
905 g_free(output_window->participant);
906 g_free(output_window);
908 return TRUE;
909 #else
910 return FALSE;
911 #endif
914 void
915 purple_media_manager_remove_output_windows(PurpleMediaManager *manager,
916 PurpleMedia *media, const gchar *session_id,
917 const gchar *participant)
919 #ifdef USE_VV
920 GList *iter;
922 g_return_if_fail(PURPLE_IS_MEDIA(media));
924 iter = manager->priv->output_windows;
926 for (; iter;) {
927 PurpleMediaOutputWindow *ow = iter->data;
928 iter = g_list_next(iter);
930 if (media == ow->media &&
931 ((session_id != NULL && ow->session_id != NULL &&
932 !strcmp(session_id, ow->session_id)) ||
933 (session_id == ow->session_id)) &&
934 ((participant != NULL && ow->participant != NULL &&
935 !strcmp(participant, ow->participant)) ||
936 (participant == ow->participant)))
937 purple_media_manager_remove_output_window(
938 manager, ow->id);
940 #endif
943 void
944 purple_media_manager_set_ui_caps(PurpleMediaManager *manager,
945 PurpleMediaCaps caps)
947 #ifdef USE_VV
948 PurpleMediaCaps oldcaps;
950 g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager));
952 oldcaps = manager->priv->ui_caps;
953 manager->priv->ui_caps = caps;
955 if (caps != oldcaps)
956 g_signal_emit(manager,
957 purple_media_manager_signals[UI_CAPS_CHANGED],
958 0, caps, oldcaps);
959 #endif
962 PurpleMediaCaps
963 purple_media_manager_get_ui_caps(PurpleMediaManager *manager)
965 #ifdef USE_VV
966 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager),
967 PURPLE_MEDIA_CAPS_NONE);
968 return manager->priv->ui_caps;
969 #else
970 return PURPLE_MEDIA_CAPS_NONE;
971 #endif
974 void
975 purple_media_manager_set_backend_type(PurpleMediaManager *manager,
976 GType backend_type)
978 #ifdef USE_VV
979 g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager));
981 manager->priv->backend_type = backend_type;
982 #endif
985 GType
986 purple_media_manager_get_backend_type(PurpleMediaManager *manager)
988 #ifdef USE_VV
989 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager),
990 PURPLE_MEDIA_CAPS_NONE);
992 return manager->priv->backend_type;
993 #else
994 return G_TYPE_NONE;
995 #endif
998 #ifdef USE_GSTREAMER
1001 * PurpleMediaElementType
1004 GType
1005 purple_media_element_type_get_type()
1007 static GType type = 0;
1008 if (type == 0) {
1009 static const GFlagsValue values[] = {
1010 { PURPLE_MEDIA_ELEMENT_NONE,
1011 "PURPLE_MEDIA_ELEMENT_NONE", "none" },
1012 { PURPLE_MEDIA_ELEMENT_AUDIO,
1013 "PURPLE_MEDIA_ELEMENT_AUDIO", "audio" },
1014 { PURPLE_MEDIA_ELEMENT_VIDEO,
1015 "PURPLE_MEDIA_ELEMENT_VIDEO", "video" },
1016 { PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO,
1017 "PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO",
1018 "audio-video" },
1019 { PURPLE_MEDIA_ELEMENT_NO_SRCS,
1020 "PURPLE_MEDIA_ELEMENT_NO_SRCS", "no-srcs" },
1021 { PURPLE_MEDIA_ELEMENT_ONE_SRC,
1022 "PURPLE_MEDIA_ELEMENT_ONE_SRC", "one-src" },
1023 { PURPLE_MEDIA_ELEMENT_MULTI_SRC,
1024 "PURPLE_MEDIA_ELEMENT_MULTI_SRC",
1025 "multi-src" },
1026 { PURPLE_MEDIA_ELEMENT_REQUEST_SRC,
1027 "PURPLE_MEDIA_ELEMENT_REQUEST_SRC",
1028 "request-src" },
1029 { PURPLE_MEDIA_ELEMENT_NO_SINKS,
1030 "PURPLE_MEDIA_ELEMENT_NO_SINKS", "no-sinks" },
1031 { PURPLE_MEDIA_ELEMENT_ONE_SINK,
1032 "PURPLE_MEDIA_ELEMENT_ONE_SINK", "one-sink" },
1033 { PURPLE_MEDIA_ELEMENT_MULTI_SINK,
1034 "PURPLE_MEDIA_ELEMENT_MULTI_SINK",
1035 "multi-sink" },
1036 { PURPLE_MEDIA_ELEMENT_REQUEST_SINK,
1037 "PURPLE_MEDIA_ELEMENT_REQUEST_SINK",
1038 "request-sink" },
1039 { PURPLE_MEDIA_ELEMENT_UNIQUE,
1040 "PURPLE_MEDIA_ELEMENT_UNIQUE", "unique" },
1041 { PURPLE_MEDIA_ELEMENT_SRC,
1042 "PURPLE_MEDIA_ELEMENT_SRC", "src" },
1043 { PURPLE_MEDIA_ELEMENT_SINK,
1044 "PURPLE_MEDIA_ELEMENT_SINK", "sink" },
1045 { 0, NULL, NULL }
1047 type = g_flags_register_static(
1048 "PurpleMediaElementType", values);
1050 return type;
1054 * PurpleMediaElementInfo
1057 struct _PurpleMediaElementInfoClass
1059 GObjectClass parent_class;
1062 struct _PurpleMediaElementInfo
1064 GObject parent;
1067 #ifdef USE_VV
1068 struct _PurpleMediaElementInfoPrivate
1070 gchar *id;
1071 gchar *name;
1072 PurpleMediaElementType type;
1073 PurpleMediaElementCreateCallback create;
1076 enum {
1077 PROP_0,
1078 PROP_ID,
1079 PROP_NAME,
1080 PROP_TYPE,
1081 PROP_CREATE_CB,
1084 static void
1085 purple_media_element_info_init(PurpleMediaElementInfo *info)
1087 PurpleMediaElementInfoPrivate *priv =
1088 PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(info);
1089 priv->id = NULL;
1090 priv->name = NULL;
1091 priv->type = PURPLE_MEDIA_ELEMENT_NONE;
1092 priv->create = NULL;
1095 static void
1096 purple_media_element_info_finalize(GObject *info)
1098 PurpleMediaElementInfoPrivate *priv =
1099 PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(info);
1100 g_free(priv->id);
1101 g_free(priv->name);
1104 static void
1105 purple_media_element_info_set_property (GObject *object, guint prop_id,
1106 const GValue *value, GParamSpec *pspec)
1108 PurpleMediaElementInfoPrivate *priv;
1109 g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object));
1111 priv = PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(object);
1113 switch (prop_id) {
1114 case PROP_ID:
1115 g_free(priv->id);
1116 priv->id = g_value_dup_string(value);
1117 break;
1118 case PROP_NAME:
1119 g_free(priv->name);
1120 priv->name = g_value_dup_string(value);
1121 break;
1122 case PROP_TYPE: {
1123 priv->type = g_value_get_flags(value);
1124 break;
1126 case PROP_CREATE_CB:
1127 priv->create = g_value_get_pointer(value);
1128 break;
1129 default:
1130 G_OBJECT_WARN_INVALID_PROPERTY_ID(
1131 object, prop_id, pspec);
1132 break;
1136 static void
1137 purple_media_element_info_get_property (GObject *object, guint prop_id,
1138 GValue *value, GParamSpec *pspec)
1140 PurpleMediaElementInfoPrivate *priv;
1141 g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object));
1143 priv = PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(object);
1145 switch (prop_id) {
1146 case PROP_ID:
1147 g_value_set_string(value, priv->id);
1148 break;
1149 case PROP_NAME:
1150 g_value_set_string(value, priv->name);
1151 break;
1152 case PROP_TYPE:
1153 g_value_set_flags(value, priv->type);
1154 break;
1155 case PROP_CREATE_CB:
1156 g_value_set_pointer(value, priv->create);
1157 break;
1158 default:
1159 G_OBJECT_WARN_INVALID_PROPERTY_ID(
1160 object, prop_id, pspec);
1161 break;
1165 static void
1166 purple_media_element_info_class_init(PurpleMediaElementInfoClass *klass)
1168 GObjectClass *gobject_class = (GObjectClass*)klass;
1170 gobject_class->finalize = purple_media_element_info_finalize;
1171 gobject_class->set_property = purple_media_element_info_set_property;
1172 gobject_class->get_property = purple_media_element_info_get_property;
1174 g_object_class_install_property(gobject_class, PROP_ID,
1175 g_param_spec_string("id",
1176 "ID",
1177 "The unique identifier of the element.",
1178 NULL,
1179 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1181 g_object_class_install_property(gobject_class, PROP_NAME,
1182 g_param_spec_string("name",
1183 "Name",
1184 "The friendly/display name of this element.",
1185 NULL,
1186 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1188 g_object_class_install_property(gobject_class, PROP_TYPE,
1189 g_param_spec_flags("type",
1190 "Element Type",
1191 "The type of element this is.",
1192 PURPLE_TYPE_MEDIA_ELEMENT_TYPE,
1193 PURPLE_MEDIA_ELEMENT_NONE,
1194 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1196 g_object_class_install_property(gobject_class, PROP_CREATE_CB,
1197 g_param_spec_pointer("create-cb",
1198 "Create Callback",
1199 "The function called to create this element.",
1200 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1202 g_type_class_add_private(klass, sizeof(PurpleMediaElementInfoPrivate));
1205 G_DEFINE_TYPE(PurpleMediaElementInfo,
1206 purple_media_element_info, G_TYPE_OBJECT);
1207 #else
1208 GType
1209 purple_media_element_info_get_type()
1211 return G_TYPE_NONE;
1213 #endif
1215 gchar *
1216 purple_media_element_info_get_id(PurpleMediaElementInfo *info)
1218 #ifdef USE_VV
1219 gchar *id;
1220 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info), NULL);
1221 g_object_get(info, "id", &id, NULL);
1222 return id;
1223 #else
1224 return NULL;
1225 #endif
1228 gchar *
1229 purple_media_element_info_get_name(PurpleMediaElementInfo *info)
1231 #ifdef USE_VV
1232 gchar *name;
1233 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info), NULL);
1234 g_object_get(info, "name", &name, NULL);
1235 return name;
1236 #else
1237 return NULL;
1238 #endif
1241 PurpleMediaElementType
1242 purple_media_element_info_get_element_type(PurpleMediaElementInfo *info)
1244 #ifdef USE_VV
1245 PurpleMediaElementType type;
1246 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info),
1247 PURPLE_MEDIA_ELEMENT_NONE);
1248 g_object_get(info, "type", &type, NULL);
1249 return type;
1250 #else
1251 return PURPLE_MEDIA_ELEMENT_NONE;
1252 #endif
1255 GstElement *
1256 purple_media_element_info_call_create(PurpleMediaElementInfo *info,
1257 PurpleMedia *media, const gchar *session_id,
1258 const gchar *participant)
1260 #ifdef USE_VV
1261 PurpleMediaElementCreateCallback create;
1262 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info), NULL);
1263 g_object_get(info, "create-cb", &create, NULL);
1264 if (create)
1265 return create(media, session_id, participant);
1266 #endif
1267 return NULL;
1270 #endif /* USE_GSTREAMER */