comparison 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
comparison
equal deleted inserted replaced
29470:7a3458436140 29471:d83ee160ffb6
1 /**
2 * @file mediamanager.c Media Manager API
3 * @ingroup core
4 */
5
6 /* purple
7 *
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.
11 *
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.
16 *
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.
21 *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27 #include "internal.h"
28
29 #include "account.h"
30 #include "debug.h"
31 #include "media.h"
32 #include "mediamanager.h"
33
34 #ifdef USE_GSTREAMER
35 #include "marshallers.h"
36 #include "media-gst.h"
37 #endif
38
39 #ifdef USE_VV
40
41 #include <gst/farsight/fs-conference-iface.h>
42 #include <gst/farsight/fs-element-added-notifier.h>
43 #include <gst/interfaces/xoverlay.h>
44
45 /** @copydoc _PurpleMediaManagerPrivate */
46 typedef struct _PurpleMediaManagerPrivate PurpleMediaManagerPrivate;
47 /** @copydoc _PurpleMediaOutputWindow */
48 typedef struct _PurpleMediaOutputWindow PurpleMediaOutputWindow;
49 /** @copydoc _PurpleMediaManagerPrivate */
50 typedef struct _PurpleMediaElementInfoPrivate PurpleMediaElementInfoPrivate;
51
52 /** The media manager class. */
53 struct _PurpleMediaManagerClass
54 {
55 GObjectClass parent_class; /**< The parent class. */
56 };
57
58 /** The media manager's data. */
59 struct _PurpleMediaManager
60 {
61 GObject parent; /**< The parent of this manager. */
62 PurpleMediaManagerPrivate *priv; /**< Private data for the manager. */
63 };
64
65 struct _PurpleMediaOutputWindow
66 {
67 gulong id;
68 PurpleMedia *media;
69 gchar *session_id;
70 gchar *participant;
71 gulong window_id;
72 GstElement *sink;
73 };
74
75 struct _PurpleMediaManagerPrivate
76 {
77 GstElement *pipeline;
78 PurpleMediaCaps ui_caps;
79 GList *medias;
80 GList *elements;
81 GList *output_windows;
82 gulong next_output_window_id;
83
84 PurpleMediaElementInfo *video_src;
85 PurpleMediaElementInfo *video_sink;
86 PurpleMediaElementInfo *audio_src;
87 PurpleMediaElementInfo *audio_sink;
88 };
89
90 #define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate))
91 #define PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfoPrivate))
92
93 static void purple_media_manager_class_init (PurpleMediaManagerClass *klass);
94 static void purple_media_manager_init (PurpleMediaManager *media);
95 static void purple_media_manager_finalize (GObject *object);
96
97 static GObjectClass *parent_class = NULL;
98
99
100
101 enum {
102 INIT_MEDIA,
103 LAST_SIGNAL
104 };
105 static guint purple_media_manager_signals[LAST_SIGNAL] = {0};
106 #endif
107
108 GType
109 purple_media_manager_get_type()
110 {
111 #ifdef USE_VV
112 static GType type = 0;
113
114 if (type == 0) {
115 static const GTypeInfo info = {
116 sizeof(PurpleMediaManagerClass),
117 NULL,
118 NULL,
119 (GClassInitFunc) purple_media_manager_class_init,
120 NULL,
121 NULL,
122 sizeof(PurpleMediaManager),
123 0,
124 (GInstanceInitFunc) purple_media_manager_init,
125 NULL
126 };
127 type = g_type_register_static(G_TYPE_OBJECT, "PurpleMediaManager", &info, 0);
128 }
129 return type;
130 #else
131 return G_TYPE_NONE;
132 #endif
133 }
134
135 #ifdef USE_VV
136 static void
137 purple_media_manager_class_init (PurpleMediaManagerClass *klass)
138 {
139 GObjectClass *gobject_class = (GObjectClass*)klass;
140 parent_class = g_type_class_peek_parent(klass);
141
142 gobject_class->finalize = purple_media_manager_finalize;
143
144 purple_media_manager_signals[INIT_MEDIA] = g_signal_new ("init-media",
145 G_TYPE_FROM_CLASS (klass),
146 G_SIGNAL_RUN_LAST,
147 0, NULL, NULL,
148 purple_smarshal_BOOLEAN__OBJECT_POINTER_STRING,
149 G_TYPE_BOOLEAN, 3, PURPLE_TYPE_MEDIA,
150 G_TYPE_POINTER, G_TYPE_STRING);
151 g_type_class_add_private(klass, sizeof(PurpleMediaManagerPrivate));
152 }
153
154 static void
155 purple_media_manager_init (PurpleMediaManager *media)
156 {
157 media->priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media);
158 media->priv->medias = NULL;
159 media->priv->next_output_window_id = 1;
160
161 purple_prefs_add_none("/purple/media");
162 purple_prefs_add_none("/purple/media/audio");
163 purple_prefs_add_none("/purple/media/audio/volume");
164 purple_prefs_add_int("/purple/media/audio/volume/input", 10);
165 purple_prefs_add_int("/purple/media/audio/volume/output", 10);
166 }
167
168 static void
169 purple_media_manager_finalize (GObject *media)
170 {
171 PurpleMediaManagerPrivate *priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media);
172 for (; priv->medias; priv->medias =
173 g_list_delete_link(priv->medias, priv->medias)) {
174 g_object_unref(priv->medias->data);
175 }
176 for (; priv->elements; priv->elements =
177 g_list_delete_link(priv->elements, priv->elements)) {
178 g_object_unref(priv->elements->data);
179 }
180 parent_class->finalize(media);
181 }
182 #endif
183
184 PurpleMediaManager *
185 purple_media_manager_get()
186 {
187 #ifdef USE_VV
188 static PurpleMediaManager *manager = NULL;
189
190 if (manager == NULL)
191 manager = PURPLE_MEDIA_MANAGER(g_object_new(purple_media_manager_get_type(), NULL));
192 return manager;
193 #else
194 return NULL;
195 #endif
196 }
197
198 #ifdef USE_VV
199 static gboolean
200 pipeline_bus_call(GstBus *bus, GstMessage *msg, PurpleMediaManager *manager)
201 {
202 switch(GST_MESSAGE_TYPE(msg)) {
203 case GST_MESSAGE_EOS:
204 purple_debug_info("mediamanager", "End of Stream\n");
205 break;
206 case GST_MESSAGE_ERROR: {
207 gchar *debug = NULL;
208 GError *err = NULL;
209
210 gst_message_parse_error(msg, &err, &debug);
211
212 purple_debug_error("mediamanager",
213 "gst pipeline error: %s\n",
214 err->message);
215 g_error_free(err);
216
217 if (debug) {
218 purple_debug_error("mediamanager",
219 "Debug details: %s\n", debug);
220 g_free (debug);
221 }
222 break;
223 }
224 default:
225 break;
226 }
227 return TRUE;
228 }
229 #endif
230
231 #ifdef USE_GSTREAMER
232 GstElement *
233 purple_media_manager_get_pipeline(PurpleMediaManager *manager)
234 {
235 #ifdef USE_VV
236 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
237
238 if (manager->priv->pipeline == NULL) {
239 FsElementAddedNotifier *notifier;
240 gchar *filename;
241 GError *err = NULL;
242 GKeyFile *keyfile;
243 GstBus *bus;
244 manager->priv->pipeline = gst_pipeline_new(NULL);
245
246 bus = gst_pipeline_get_bus(
247 GST_PIPELINE(manager->priv->pipeline));
248 gst_bus_add_signal_watch(GST_BUS(bus));
249 g_signal_connect(G_OBJECT(bus), "message",
250 G_CALLBACK(pipeline_bus_call), manager);
251 gst_bus_set_sync_handler(bus,
252 gst_bus_sync_signal_handler, NULL);
253 gst_object_unref(bus);
254
255 filename = g_build_filename(purple_user_dir(),
256 "fs-element.conf", NULL);
257 keyfile = g_key_file_new();
258 if (!g_key_file_load_from_file(keyfile, filename,
259 G_KEY_FILE_NONE, &err)) {
260 if (err->code == 4)
261 purple_debug_info("mediamanager",
262 "Couldn't read "
263 "fs-element.conf: %s\n",
264 err->message);
265 else
266 purple_debug_error("mediamanager",
267 "Error reading "
268 "fs-element.conf: %s\n",
269 err->message);
270 g_error_free(err);
271 }
272 g_free(filename);
273
274 /* Hack to make alsasrc stop messing up audio timestamps */
275 if (!g_key_file_has_key(keyfile,
276 "alsasrc", "slave-method", NULL)) {
277 g_key_file_set_integer(keyfile,
278 "alsasrc", "slave-method", 2);
279 }
280
281 notifier = fs_element_added_notifier_new();
282 fs_element_added_notifier_add(notifier,
283 GST_BIN(manager->priv->pipeline));
284 fs_element_added_notifier_set_properties_from_keyfile(
285 notifier, keyfile);
286
287 gst_element_set_state(manager->priv->pipeline,
288 GST_STATE_PLAYING);
289 }
290
291 return manager->priv->pipeline;
292 #else
293 return NULL;
294 #endif
295 }
296 #endif /* USE_GSTREAMER */
297
298 PurpleMedia *
299 purple_media_manager_create_media(PurpleMediaManager *manager,
300 PurpleAccount *account,
301 const char *conference_type,
302 const char *remote_user,
303 gboolean initiator)
304 {
305 #ifdef USE_VV
306 PurpleMedia *media;
307 FsConference *conference = FS_CONFERENCE(gst_element_factory_make(conference_type, NULL));
308 GstStateChangeReturn ret;
309 gboolean signal_ret;
310
311 if (conference == NULL) {
312 purple_conv_present_error(remote_user, account,
313 _("Error creating conference."));
314 purple_debug_error("media", "Conference == NULL\n");
315 return NULL;
316 }
317
318 media = PURPLE_MEDIA(g_object_new(purple_media_get_type(),
319 "manager", manager,
320 "account", account,
321 "conference", conference,
322 "initiator", initiator,
323 NULL));
324
325 ret = gst_element_set_state(GST_ELEMENT(conference), GST_STATE_PLAYING);
326
327 if (ret == GST_STATE_CHANGE_FAILURE) {
328 purple_conv_present_error(remote_user, account,
329 _("Error creating conference."));
330 purple_debug_error("media", "Failed to start conference.\n");
331 g_object_unref(media);
332 return NULL;
333 }
334
335 g_signal_emit(manager, purple_media_manager_signals[INIT_MEDIA], 0,
336 media, account, remote_user, &signal_ret);
337
338 if (signal_ret == FALSE) {
339 g_object_unref(media);
340 return NULL;
341 }
342
343 manager->priv->medias = g_list_append(manager->priv->medias, media);
344 return media;
345 #else
346 return NULL;
347 #endif
348 }
349
350 GList *
351 purple_media_manager_get_media(PurpleMediaManager *manager)
352 {
353 #ifdef USE_VV
354 return manager->priv->medias;
355 #else
356 return NULL;
357 #endif
358 }
359
360 GList *
361 purple_media_manager_get_media_by_account(PurpleMediaManager *manager,
362 PurpleAccount *account)
363 {
364 #ifdef USE_VV
365 GList *media = NULL;
366 GList *iter;
367
368 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
369
370 iter = manager->priv->medias;
371 for (; iter; iter = g_list_next(iter)) {
372 if (purple_media_get_account(iter->data) == account) {
373 media = g_list_prepend(media, iter->data);
374 }
375 }
376
377 return media;
378 #else
379 return NULL;
380 #endif
381 }
382
383 void
384 purple_media_manager_remove_media(PurpleMediaManager *manager,
385 PurpleMedia *media)
386 {
387 #ifdef USE_VV
388 GList *list = g_list_find(manager->priv->medias, media);
389 if (list)
390 manager->priv->medias =
391 g_list_delete_link(manager->priv->medias, list);
392 #endif
393 }
394
395 #ifdef USE_VV
396 static void
397 request_pad_unlinked_cb(GstPad *pad, GstPad *peer, gpointer user_data)
398 {
399 GstElement *parent = GST_ELEMENT_PARENT(pad);
400 GstIterator *iter;
401 GstPad *remaining_pad;
402 GstIteratorResult result;
403
404 gst_element_release_request_pad(GST_ELEMENT_PARENT(pad), pad);
405 iter = gst_element_iterate_src_pads(parent);
406
407 result = gst_iterator_next(iter, (gpointer)&remaining_pad);
408
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);
415 }
416
417 gst_iterator_free(iter);
418 }
419 #endif
420
421 #ifdef USE_GSTREAMER
422 GstElement *
423 purple_media_manager_get_element(PurpleMediaManager *manager,
424 PurpleMediaSessionType type, PurpleMedia *media,
425 const gchar *session_id, const gchar *participant)
426 {
427 #ifdef USE_VV
428 GstElement *ret = NULL;
429 PurpleMediaElementInfo *info = NULL;
430 PurpleMediaElementType element_type;
431
432 if (type & PURPLE_MEDIA_SEND_AUDIO)
433 info = manager->priv->audio_src;
434 else if (type & PURPLE_MEDIA_RECV_AUDIO)
435 info = manager->priv->audio_sink;
436 else if (type & PURPLE_MEDIA_SEND_VIDEO)
437 info = manager->priv->video_src;
438 else if (type & PURPLE_MEDIA_RECV_VIDEO)
439 info = manager->priv->video_sink;
440
441 if (info == NULL)
442 return NULL;
443
444 element_type = purple_media_element_info_get_element_type(info);
445
446 if (element_type & PURPLE_MEDIA_ELEMENT_UNIQUE &&
447 element_type & PURPLE_MEDIA_ELEMENT_SRC) {
448 GstElement *tee;
449 GstPad *pad;
450 GstPad *ghost;
451 gchar *id = purple_media_element_info_get_id(info);
452
453 ret = gst_bin_get_by_name(GST_BIN(
454 purple_media_manager_get_pipeline(
455 manager)), id);
456
457 if (ret == NULL) {
458 GstElement *bin, *fakesink;
459 ret = purple_media_element_info_call_create(info,
460 media, session_id, participant);
461 bin = gst_bin_new(id);
462 tee = gst_element_factory_make("tee", "tee");
463 gst_bin_add_many(GST_BIN(bin), ret, tee, NULL);
464 gst_element_link(ret, tee);
465
466 /*
467 * This shouldn't be necessary, but it stops it from
468 * giving a not-linked error upon destruction
469 */
470 fakesink = gst_element_factory_make("fakesink", NULL);
471 g_object_set(fakesink, "sync", FALSE, NULL);
472 gst_bin_add(GST_BIN(bin), fakesink);
473 gst_element_link(tee, fakesink);
474
475 ret = bin;
476 gst_object_ref(ret);
477 gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(
478 manager)), ret);
479 }
480 g_free(id);
481
482 tee = gst_bin_get_by_name(GST_BIN(ret), "tee");
483 pad = gst_element_get_request_pad(tee, "src%d");
484 gst_object_unref(tee);
485 ghost = gst_ghost_pad_new(NULL, pad);
486 gst_object_unref(pad);
487 g_signal_connect(GST_PAD(ghost), "unlinked",
488 G_CALLBACK(request_pad_unlinked_cb), NULL);
489 gst_pad_set_active(ghost, TRUE);
490 gst_element_add_pad(ret, ghost);
491 } else {
492 ret = purple_media_element_info_call_create(info,
493 media, session_id, participant);
494 }
495
496 if (ret == NULL)
497 purple_debug_error("media", "Error creating source or sink\n");
498
499 return ret;
500 #else
501 return NULL;
502 #endif
503 }
504
505 PurpleMediaElementInfo *
506 purple_media_manager_get_element_info(PurpleMediaManager *manager,
507 const gchar *id)
508 {
509 #ifdef USE_VV
510 GList *iter;
511
512 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
513
514 iter = manager->priv->elements;
515
516 for (; iter; iter = g_list_next(iter)) {
517 gchar *element_id =
518 purple_media_element_info_get_id(iter->data);
519 if (!strcmp(element_id, id)) {
520 g_free(element_id);
521 g_object_ref(iter->data);
522 return iter->data;
523 }
524 g_free(element_id);
525 }
526 #endif
527
528 return NULL;
529 }
530
531 gboolean
532 purple_media_manager_register_element(PurpleMediaManager *manager,
533 PurpleMediaElementInfo *info)
534 {
535 #ifdef USE_VV
536 PurpleMediaElementInfo *info2;
537 gchar *id;
538
539 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
540 g_return_val_if_fail(info != NULL, FALSE);
541
542 id = purple_media_element_info_get_id(info);
543 info2 = purple_media_manager_get_element_info(manager, id);
544 g_free(id);
545
546 if (info2 != NULL) {
547 g_object_unref(info2);
548 return FALSE;
549 }
550
551 manager->priv->elements =
552 g_list_prepend(manager->priv->elements, info);
553 return TRUE;
554 #else
555 return FALSE;
556 #endif
557 }
558
559 gboolean
560 purple_media_manager_unregister_element(PurpleMediaManager *manager,
561 const gchar *id)
562 {
563 #ifdef USE_VV
564 PurpleMediaElementInfo *info;
565
566 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
567
568 info = purple_media_manager_get_element_info(manager, id);
569
570 if (info == NULL) {
571 g_object_unref(info);
572 return FALSE;
573 }
574
575 if (manager->priv->audio_src == info)
576 manager->priv->audio_src = NULL;
577 if (manager->priv->audio_sink == info)
578 manager->priv->audio_sink = NULL;
579 if (manager->priv->video_src == info)
580 manager->priv->video_src = NULL;
581 if (manager->priv->video_sink == info)
582 manager->priv->video_sink = NULL;
583
584 manager->priv->elements = g_list_remove(
585 manager->priv->elements, info);
586 g_object_unref(info);
587 return TRUE;
588 #else
589 return FALSE;
590 #endif
591 }
592
593 gboolean
594 purple_media_manager_set_active_element(PurpleMediaManager *manager,
595 PurpleMediaElementInfo *info)
596 {
597 #ifdef USE_VV
598 PurpleMediaElementInfo *info2;
599 PurpleMediaElementType type;
600 gboolean ret = FALSE;
601 gchar *id;
602
603 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
604 g_return_val_if_fail(info != NULL, FALSE);
605
606 id = purple_media_element_info_get_id(info);
607 info2 = purple_media_manager_get_element_info(manager, id);
608 g_free(id);
609
610 if (info2 == NULL)
611 purple_media_manager_register_element(manager, info);
612 else
613 g_object_unref(info2);
614
615 type = purple_media_element_info_get_element_type(info);
616
617 if (type & PURPLE_MEDIA_ELEMENT_SRC) {
618 if (type & PURPLE_MEDIA_ELEMENT_AUDIO) {
619 manager->priv->audio_src = info;
620 ret = TRUE;
621 }
622 if (type & PURPLE_MEDIA_ELEMENT_VIDEO) {
623 manager->priv->video_src = info;
624 ret = TRUE;
625 }
626 }
627 if (type & PURPLE_MEDIA_ELEMENT_SINK) {
628 if (type & PURPLE_MEDIA_ELEMENT_AUDIO) {
629 manager->priv->audio_sink = info;
630 ret = TRUE;
631 }
632 if (type & PURPLE_MEDIA_ELEMENT_VIDEO) {
633 manager->priv->video_sink = info;
634 ret = TRUE;
635 }
636 }
637
638 return ret;
639 #else
640 return FALSE;
641 #endif
642 }
643
644 PurpleMediaElementInfo *
645 purple_media_manager_get_active_element(PurpleMediaManager *manager,
646 PurpleMediaElementType type)
647 {
648 #ifdef USE_VV
649 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
650
651 if (type & PURPLE_MEDIA_ELEMENT_SRC) {
652 if (type & PURPLE_MEDIA_ELEMENT_AUDIO)
653 return manager->priv->audio_src;
654 else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
655 return manager->priv->video_src;
656 } else if (type & PURPLE_MEDIA_ELEMENT_SINK) {
657 if (type & PURPLE_MEDIA_ELEMENT_AUDIO)
658 return manager->priv->audio_sink;
659 else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
660 return manager->priv->video_sink;
661 }
662 #endif
663
664 return NULL;
665 }
666 #endif /* USE_GSTREAMER */
667
668 #ifdef USE_VV
669 static void
670 window_id_cb(GstBus *bus, GstMessage *msg, PurpleMediaOutputWindow *ow)
671 {
672 GstElement *sink;
673
674 if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT ||
675 !gst_structure_has_name(msg->structure,
676 "prepare-xwindow-id"))
677 return;
678
679 sink = GST_ELEMENT(GST_MESSAGE_SRC(msg));
680 while (sink != ow->sink) {
681 if (sink == NULL)
682 return;
683 sink = GST_ELEMENT_PARENT(sink);
684 }
685
686 g_signal_handlers_disconnect_matched(bus, G_SIGNAL_MATCH_FUNC
687 | G_SIGNAL_MATCH_DATA, 0, 0, NULL,
688 window_id_cb, ow);
689
690 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(
691 GST_MESSAGE_SRC(msg)), ow->window_id);
692 }
693 #endif
694
695 gboolean
696 purple_media_manager_create_output_window(PurpleMediaManager *manager,
697 PurpleMedia *media, const gchar *session_id,
698 const gchar *participant)
699 {
700 #ifdef USE_VV
701 GList *iter;
702
703 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
704
705 iter = manager->priv->output_windows;
706 for(; iter; iter = g_list_next(iter)) {
707 PurpleMediaOutputWindow *ow = iter->data;
708
709 if (ow->sink == NULL && ow->media == media &&
710 ((participant != NULL &&
711 ow->participant != NULL &&
712 !strcmp(participant, ow->participant)) ||
713 (participant == ow->participant)) &&
714 !strcmp(session_id, ow->session_id)) {
715 GstBus *bus;
716 GstElement *queue, *colorspace;
717 GstElement *tee = purple_media_get_tee(media,
718 session_id, participant);
719
720 if (tee == NULL)
721 continue;
722
723 queue = gst_element_factory_make(
724 "queue", NULL);
725 colorspace = gst_element_factory_make(
726 "ffmpegcolorspace", NULL);
727 ow->sink = purple_media_manager_get_element(
728 manager, PURPLE_MEDIA_RECV_VIDEO,
729 ow->media, ow->session_id,
730 ow->participant);
731
732 if (participant == NULL) {
733 /* aka this is a preview sink */
734 GObjectClass *klass =
735 G_OBJECT_GET_CLASS(ow->sink);
736 if (g_object_class_find_property(klass,
737 "sync"))
738 g_object_set(G_OBJECT(ow->sink),
739 "sync", "FALSE", NULL);
740 if (g_object_class_find_property(klass,
741 "async"))
742 g_object_set(G_OBJECT(ow->sink),
743 "async", FALSE, NULL);
744 }
745
746 gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee)),
747 queue, colorspace, ow->sink, NULL);
748
749 bus = gst_pipeline_get_bus(GST_PIPELINE(
750 manager->priv->pipeline));
751 g_signal_connect(bus, "sync-message::element",
752 G_CALLBACK(window_id_cb), ow);
753 gst_object_unref(bus);
754
755 gst_element_set_state(ow->sink, GST_STATE_PLAYING);
756 gst_element_set_state(colorspace, GST_STATE_PLAYING);
757 gst_element_set_state(queue, GST_STATE_PLAYING);
758 gst_element_link(colorspace, ow->sink);
759 gst_element_link(queue, colorspace);
760 gst_element_link(tee, queue);
761 }
762 }
763 return TRUE;
764 #else
765 return FALSE;
766 #endif
767 }
768
769 gulong
770 purple_media_manager_set_output_window(PurpleMediaManager *manager,
771 PurpleMedia *media, const gchar *session_id,
772 const gchar *participant, gulong window_id)
773 {
774 #ifdef USE_VV
775 PurpleMediaOutputWindow *output_window;
776
777 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
778 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
779
780 output_window = g_new0(PurpleMediaOutputWindow, 1);
781 output_window->id = manager->priv->next_output_window_id++;
782 output_window->media = media;
783 output_window->session_id = g_strdup(session_id);
784 output_window->participant = g_strdup(participant);
785 output_window->window_id = window_id;
786
787 manager->priv->output_windows = g_list_prepend(
788 manager->priv->output_windows, output_window);
789
790 if (purple_media_get_tee(media, session_id, participant) != NULL)
791 purple_media_manager_create_output_window(manager,
792 media, session_id, participant);
793
794 return output_window->id;
795 #else
796 return 0;
797 #endif
798 }
799
800 gboolean
801 purple_media_manager_remove_output_window(PurpleMediaManager *manager,
802 gulong output_window_id)
803 {
804 #ifdef USE_VV
805 PurpleMediaOutputWindow *output_window = NULL;
806 GList *iter;
807
808 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
809
810 iter = manager->priv->output_windows;
811 for (; iter; iter = g_list_next(iter)) {
812 PurpleMediaOutputWindow *ow = iter->data;
813 if (ow->id == output_window_id) {
814 manager->priv->output_windows = g_list_delete_link(
815 manager->priv->output_windows, iter);
816 output_window = ow;
817 break;
818 }
819 }
820
821 if (output_window == NULL)
822 return FALSE;
823
824 if (output_window->sink != NULL) {
825 GstPad *pad = gst_element_get_static_pad(
826 output_window->sink, "sink");
827 GstPad *peer = gst_pad_get_peer(pad);
828 GstElement *colorspace = GST_ELEMENT_PARENT(peer), *queue;
829 gst_object_unref(pad);
830 gst_object_unref(peer);
831 pad = gst_element_get_static_pad(colorspace, "sink");
832 peer = gst_pad_get_peer(pad);
833 queue = GST_ELEMENT_PARENT(peer);
834 gst_object_unref(pad);
835 gst_object_unref(peer);
836 pad = gst_element_get_static_pad(queue, "sink");
837 peer = gst_pad_get_peer(pad);
838 gst_object_unref(pad);
839 if (peer != NULL)
840 gst_element_release_request_pad(GST_ELEMENT_PARENT(peer), peer);
841 gst_element_set_locked_state(queue, TRUE);
842 gst_element_set_state(queue, GST_STATE_NULL);
843 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(queue)), queue);
844 gst_element_set_locked_state(colorspace, TRUE);
845 gst_element_set_state(colorspace, GST_STATE_NULL);
846 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(colorspace)), colorspace);
847 gst_element_set_locked_state(output_window->sink, TRUE);
848 gst_element_set_state(output_window->sink, GST_STATE_NULL);
849 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(output_window->sink)),
850 output_window->sink);
851 }
852
853 g_free(output_window->session_id);
854 g_free(output_window->participant);
855 g_free(output_window);
856
857 return TRUE;
858 #else
859 return FALSE;
860 #endif
861 }
862
863 void
864 purple_media_manager_remove_output_windows(PurpleMediaManager *manager,
865 PurpleMedia *media, const gchar *session_id,
866 const gchar *participant)
867 {
868 #ifdef USE_VV
869 GList *iter;
870
871 g_return_if_fail(PURPLE_IS_MEDIA(media));
872
873 iter = manager->priv->output_windows;
874
875 for (; iter;) {
876 PurpleMediaOutputWindow *ow = iter->data;
877 iter = g_list_next(iter);
878
879 if (media == ow->media &&
880 ((session_id != NULL && ow->session_id != NULL &&
881 !strcmp(session_id, ow->session_id)) ||
882 (session_id == ow->session_id)) &&
883 ((participant != NULL && ow->participant != NULL &&
884 !strcmp(participant, ow->participant)) ||
885 (participant == ow->participant)))
886 purple_media_manager_remove_output_window(
887 manager, ow->id);
888 }
889 #endif
890 }
891
892 void
893 purple_media_manager_set_ui_caps(PurpleMediaManager *manager,
894 PurpleMediaCaps caps)
895 {
896 #ifdef USE_VV
897 g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager));
898 manager->priv->ui_caps = caps;
899 #endif
900 }
901
902 PurpleMediaCaps
903 purple_media_manager_get_ui_caps(PurpleMediaManager *manager)
904 {
905 #ifdef USE_VV
906 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager),
907 PURPLE_MEDIA_CAPS_NONE);
908 return manager->priv->ui_caps;
909 #else
910 return PURPLE_MEDIA_CAPS_NONE;
911 #endif
912 }
913
914 #ifdef USE_GSTREAMER
915
916 /*
917 * PurpleMediaElementType
918 */
919
920 GType
921 purple_media_element_type_get_type()
922 {
923 static GType type = 0;
924 if (type == 0) {
925 static const GFlagsValue values[] = {
926 { PURPLE_MEDIA_ELEMENT_NONE,
927 "PURPLE_MEDIA_ELEMENT_NONE", "none" },
928 { PURPLE_MEDIA_ELEMENT_AUDIO,
929 "PURPLE_MEDIA_ELEMENT_AUDIO", "audio" },
930 { PURPLE_MEDIA_ELEMENT_VIDEO,
931 "PURPLE_MEDIA_ELEMENT_VIDEO", "video" },
932 { PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO,
933 "PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO",
934 "audio-video" },
935 { PURPLE_MEDIA_ELEMENT_NO_SRCS,
936 "PURPLE_MEDIA_ELEMENT_NO_SRCS", "no-srcs" },
937 { PURPLE_MEDIA_ELEMENT_ONE_SRC,
938 "PURPLE_MEDIA_ELEMENT_ONE_SRC", "one-src" },
939 { PURPLE_MEDIA_ELEMENT_MULTI_SRC,
940 "PURPLE_MEDIA_ELEMENT_MULTI_SRC",
941 "multi-src" },
942 { PURPLE_MEDIA_ELEMENT_REQUEST_SRC,
943 "PURPLE_MEDIA_ELEMENT_REQUEST_SRC",
944 "request-src" },
945 { PURPLE_MEDIA_ELEMENT_NO_SINKS,
946 "PURPLE_MEDIA_ELEMENT_NO_SINKS", "no-sinks" },
947 { PURPLE_MEDIA_ELEMENT_ONE_SINK,
948 "PURPLE_MEDIA_ELEMENT_ONE_SINK", "one-sink" },
949 { PURPLE_MEDIA_ELEMENT_MULTI_SINK,
950 "PURPLE_MEDIA_ELEMENT_MULTI_SINK",
951 "multi-sink" },
952 { PURPLE_MEDIA_ELEMENT_REQUEST_SINK,
953 "PURPLE_MEDIA_ELEMENT_REQUEST_SINK",
954 "request-sink" },
955 { PURPLE_MEDIA_ELEMENT_UNIQUE,
956 "PURPLE_MEDIA_ELEMENT_UNIQUE", "unique" },
957 { PURPLE_MEDIA_ELEMENT_SRC,
958 "PURPLE_MEDIA_ELEMENT_SRC", "src" },
959 { PURPLE_MEDIA_ELEMENT_SINK,
960 "PURPLE_MEDIA_ELEMENT_SINK", "sink" },
961 { 0, NULL, NULL }
962 };
963 type = g_flags_register_static(
964 "PurpleMediaElementType", values);
965 }
966 return type;
967 }
968
969 /*
970 * PurpleMediaElementInfo
971 */
972
973 struct _PurpleMediaElementInfoClass
974 {
975 GObjectClass parent_class;
976 };
977
978 struct _PurpleMediaElementInfo
979 {
980 GObject parent;
981 };
982
983 #ifdef USE_VV
984 struct _PurpleMediaElementInfoPrivate
985 {
986 gchar *id;
987 gchar *name;
988 PurpleMediaElementType type;
989 PurpleMediaElementCreateCallback create;
990 };
991
992 enum {
993 PROP_0,
994 PROP_ID,
995 PROP_NAME,
996 PROP_TYPE,
997 PROP_CREATE_CB,
998 };
999
1000 static void
1001 purple_media_element_info_init(PurpleMediaElementInfo *info)
1002 {
1003 PurpleMediaElementInfoPrivate *priv =
1004 PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(info);
1005 priv->id = NULL;
1006 priv->name = NULL;
1007 priv->type = PURPLE_MEDIA_ELEMENT_NONE;
1008 priv->create = NULL;
1009 }
1010
1011 static void
1012 purple_media_element_info_finalize(GObject *info)
1013 {
1014 PurpleMediaElementInfoPrivate *priv =
1015 PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(info);
1016 g_free(priv->id);
1017 g_free(priv->name);
1018 }
1019
1020 static void
1021 purple_media_element_info_set_property (GObject *object, guint prop_id,
1022 const GValue *value, GParamSpec *pspec)
1023 {
1024 PurpleMediaElementInfoPrivate *priv;
1025 g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object));
1026
1027 priv = PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(object);
1028
1029 switch (prop_id) {
1030 case PROP_ID:
1031 g_free(priv->id);
1032 priv->id = g_value_dup_string(value);
1033 break;
1034 case PROP_NAME:
1035 g_free(priv->name);
1036 priv->name = g_value_dup_string(value);
1037 break;
1038 case PROP_TYPE: {
1039 priv->type = g_value_get_flags(value);
1040 break;
1041 }
1042 case PROP_CREATE_CB:
1043 priv->create = g_value_get_pointer(value);
1044 break;
1045 default:
1046 G_OBJECT_WARN_INVALID_PROPERTY_ID(
1047 object, prop_id, pspec);
1048 break;
1049 }
1050 }
1051
1052 static void
1053 purple_media_element_info_get_property (GObject *object, guint prop_id,
1054 GValue *value, GParamSpec *pspec)
1055 {
1056 PurpleMediaElementInfoPrivate *priv;
1057 g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object));
1058
1059 priv = PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(object);
1060
1061 switch (prop_id) {
1062 case PROP_ID:
1063 g_value_set_string(value, priv->id);
1064 break;
1065 case PROP_NAME:
1066 g_value_set_string(value, priv->name);
1067 break;
1068 case PROP_TYPE:
1069 g_value_set_flags(value, priv->type);
1070 break;
1071 case PROP_CREATE_CB:
1072 g_value_set_pointer(value, priv->create);
1073 break;
1074 default:
1075 G_OBJECT_WARN_INVALID_PROPERTY_ID(
1076 object, prop_id, pspec);
1077 break;
1078 }
1079 }
1080
1081 static void
1082 purple_media_element_info_class_init(PurpleMediaElementInfoClass *klass)
1083 {
1084 GObjectClass *gobject_class = (GObjectClass*)klass;
1085
1086 gobject_class->finalize = purple_media_element_info_finalize;
1087 gobject_class->set_property = purple_media_element_info_set_property;
1088 gobject_class->get_property = purple_media_element_info_get_property;
1089
1090 g_object_class_install_property(gobject_class, PROP_ID,
1091 g_param_spec_string("id",
1092 "ID",
1093 "The unique identifier of the element.",
1094 NULL,
1095 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1096
1097 g_object_class_install_property(gobject_class, PROP_NAME,
1098 g_param_spec_string("name",
1099 "Name",
1100 "The friendly/display name of this element.",
1101 NULL,
1102 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1103
1104 g_object_class_install_property(gobject_class, PROP_TYPE,
1105 g_param_spec_flags("type",
1106 "Element Type",
1107 "The type of element this is.",
1108 PURPLE_TYPE_MEDIA_ELEMENT_TYPE,
1109 PURPLE_MEDIA_ELEMENT_NONE,
1110 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1111
1112 g_object_class_install_property(gobject_class, PROP_CREATE_CB,
1113 g_param_spec_pointer("create-cb",
1114 "Create Callback",
1115 "The function called to create this element.",
1116 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
1117
1118 g_type_class_add_private(klass, sizeof(PurpleMediaElementInfoPrivate));
1119 }
1120
1121 G_DEFINE_TYPE(PurpleMediaElementInfo,
1122 purple_media_element_info, G_TYPE_OBJECT);
1123 #else
1124 GType
1125 purple_media_element_info_get_type()
1126 {
1127 return G_TYPE_NONE;
1128 }
1129 #endif
1130
1131 gchar *
1132 purple_media_element_info_get_id(PurpleMediaElementInfo *info)
1133 {
1134 #ifdef USE_VV
1135 gchar *id;
1136 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info), NULL);
1137 g_object_get(info, "id", &id, NULL);
1138 return id;
1139 #else
1140 return NULL;
1141 #endif
1142 }
1143
1144 gchar *
1145 purple_media_element_info_get_name(PurpleMediaElementInfo *info)
1146 {
1147 #ifdef USE_VV
1148 gchar *name;
1149 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info), NULL);
1150 g_object_get(info, "name", &name, NULL);
1151 return name;
1152 #else
1153 return NULL;
1154 #endif
1155 }
1156
1157 PurpleMediaElementType
1158 purple_media_element_info_get_element_type(PurpleMediaElementInfo *info)
1159 {
1160 #ifdef USE_VV
1161 PurpleMediaElementType type;
1162 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info),
1163 PURPLE_MEDIA_ELEMENT_NONE);
1164 g_object_get(info, "type", &type, NULL);
1165 return type;
1166 #else
1167 return PURPLE_MEDIA_ELEMENT_NONE;
1168 #endif
1169 }
1170
1171 GstElement *
1172 purple_media_element_info_call_create(PurpleMediaElementInfo *info,
1173 PurpleMedia *media, const gchar *session_id,
1174 const gchar *participant)
1175 {
1176 #ifdef USE_VV
1177 PurpleMediaElementCreateCallback create;
1178 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info), NULL);
1179 g_object_get(info, "create-cb", &create, NULL);
1180 if (create)
1181 return create(media, session_id, participant);
1182 #endif
1183 return NULL;
1184 }
1185
1186 #endif /* USE_GSTREAMER */
1187