comparison libpurple/mediamanager.c @ 26346:7b63af454f26

propagate from branch 'im.pidgin.pidgin' (head 5592cb6a8b667422747bafd555fea0aed19931b6) to branch 'im.pidgin.pidgin.vv' (head ff94dfb7c44b84f5d9b4590cc429d93198703b83)
author Mike Ruprecht <maiku@soc.pidgin.im>
date Sun, 22 Mar 2009 23:33:42 +0000
parents 5ee7e8f209bb
children 1ae3af12095a
comparison
equal deleted inserted replaced
26196:399776a9ad98 26346:7b63af454f26
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 "connection.h"
30 #include "debug.h"
31 #include "marshallers.h"
32 #include "mediamanager.h"
33 #include "media.h"
34
35 #ifdef USE_VV
36
37 #include <gst/farsight/fs-conference-iface.h>
38 #include <gst/interfaces/xoverlay.h>
39
40 typedef struct _PurpleMediaOutputWindow PurpleMediaOutputWindow;
41
42 struct _PurpleMediaOutputWindow
43 {
44 gulong id;
45 PurpleMedia *media;
46 gchar *session_id;
47 gchar *participant;
48 gulong window_id;
49 GstElement *sink;
50 };
51
52 struct _PurpleMediaManagerPrivate
53 {
54 GstElement *pipeline;
55 GList *medias;
56 GList *elements;
57 GList *output_windows;
58 gulong next_output_window_id;
59
60 PurpleMediaElementInfo *video_src;
61 PurpleMediaElementInfo *video_sink;
62 PurpleMediaElementInfo *audio_src;
63 PurpleMediaElementInfo *audio_sink;
64 };
65
66 #define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate))
67
68 static void purple_media_manager_class_init (PurpleMediaManagerClass *klass);
69 static void purple_media_manager_init (PurpleMediaManager *media);
70 static void purple_media_manager_finalize (GObject *object);
71
72 static GObjectClass *parent_class = NULL;
73
74
75
76 enum {
77 INIT_MEDIA,
78 LAST_SIGNAL
79 };
80 static guint purple_media_manager_signals[LAST_SIGNAL] = {0};
81
82 enum {
83 PROP_0,
84 PROP_FARSIGHT_SESSION,
85 PROP_NAME,
86 PROP_CONNECTION,
87 PROP_MIC_ELEMENT,
88 PROP_SPEAKER_ELEMENT,
89 };
90
91 GType
92 purple_media_manager_get_type()
93 {
94 static GType type = 0;
95
96 if (type == 0) {
97 static const GTypeInfo info = {
98 sizeof(PurpleMediaManagerClass),
99 NULL,
100 NULL,
101 (GClassInitFunc) purple_media_manager_class_init,
102 NULL,
103 NULL,
104 sizeof(PurpleMediaManager),
105 0,
106 (GInstanceInitFunc) purple_media_manager_init,
107 NULL
108 };
109 type = g_type_register_static(G_TYPE_OBJECT, "PurpleMediaManager", &info, 0);
110 }
111 return type;
112 }
113
114
115 static void
116 purple_media_manager_class_init (PurpleMediaManagerClass *klass)
117 {
118 GObjectClass *gobject_class = (GObjectClass*)klass;
119 parent_class = g_type_class_peek_parent(klass);
120
121 gobject_class->finalize = purple_media_manager_finalize;
122
123 purple_media_manager_signals[INIT_MEDIA] = g_signal_new ("init-media",
124 G_TYPE_FROM_CLASS (klass),
125 G_SIGNAL_RUN_LAST,
126 0, NULL, NULL,
127 purple_smarshal_BOOLEAN__OBJECT_POINTER_STRING,
128 G_TYPE_BOOLEAN, 3, PURPLE_TYPE_MEDIA,
129 G_TYPE_POINTER, G_TYPE_STRING);
130 g_type_class_add_private(klass, sizeof(PurpleMediaManagerPrivate));
131 }
132
133 static void
134 purple_media_manager_init (PurpleMediaManager *media)
135 {
136 media->priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media);
137 media->priv->medias = NULL;
138 media->priv->next_output_window_id = 1;
139 }
140
141 static void
142 purple_media_manager_finalize (GObject *media)
143 {
144 PurpleMediaManagerPrivate *priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media);
145 for (; priv->medias; priv->medias =
146 g_list_delete_link(priv->medias, priv->medias)) {
147 g_object_unref(priv->medias->data);
148 }
149 for (; priv->elements; priv->elements =
150 g_list_delete_link(priv->elements, priv->elements));
151 parent_class->finalize(media);
152 }
153
154 PurpleMediaManager *
155 purple_media_manager_get()
156 {
157 static PurpleMediaManager *manager = NULL;
158
159 if (manager == NULL)
160 manager = PURPLE_MEDIA_MANAGER(g_object_new(purple_media_manager_get_type(), NULL));
161 return manager;
162 }
163
164 static gboolean
165 pipeline_bus_call(GstBus *bus, GstMessage *msg, PurpleMediaManager *manager)
166 {
167 switch(GST_MESSAGE_TYPE(msg)) {
168 case GST_MESSAGE_EOS:
169 purple_debug_info("mediamanager", "End of Stream\n");
170 break;
171 case GST_MESSAGE_ERROR: {
172 gchar *debug = NULL;
173 GError *err = NULL;
174
175 gst_message_parse_error(msg, &err, &debug);
176
177 purple_debug_error("mediamanager",
178 "gst pipeline error: %s\n",
179 err->message);
180 g_error_free(err);
181
182 if (debug) {
183 purple_debug_error("mediamanager",
184 "Debug details: %s\n", debug);
185 g_free (debug);
186 }
187 break;
188 }
189 default:
190 break;
191 }
192 return TRUE;
193 }
194
195 GstElement *
196 purple_media_manager_get_pipeline(PurpleMediaManager *manager)
197 {
198 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
199
200 if (manager->priv->pipeline == NULL) {
201 GstBus *bus;
202 manager->priv->pipeline = gst_pipeline_new(NULL);
203
204 bus = gst_pipeline_get_bus(
205 GST_PIPELINE(manager->priv->pipeline));
206 gst_bus_add_signal_watch(GST_BUS(bus));
207 g_signal_connect(G_OBJECT(bus), "message",
208 G_CALLBACK(pipeline_bus_call), manager);
209 gst_bus_set_sync_handler(bus,
210 gst_bus_sync_signal_handler, NULL);
211 gst_object_unref(bus);
212
213 gst_element_set_state(manager->priv->pipeline,
214 GST_STATE_PLAYING);
215 }
216
217 return manager->priv->pipeline;
218 }
219
220 PurpleMedia *
221 purple_media_manager_create_media(PurpleMediaManager *manager,
222 PurpleConnection *gc,
223 const char *conference_type,
224 const char *remote_user,
225 gboolean initiator)
226 {
227 PurpleMedia *media;
228 FsConference *conference = FS_CONFERENCE(gst_element_factory_make(conference_type, NULL));
229 GstStateChangeReturn ret;
230 gboolean signal_ret;
231
232 if (conference == NULL) {
233 purple_conv_present_error(remote_user,
234 purple_connection_get_account(gc),
235 _("Error creating conference."));
236 purple_debug_error("media", "Conference == NULL\n");
237 return NULL;
238 }
239
240 media = PURPLE_MEDIA(g_object_new(purple_media_get_type(),
241 "manager", manager,
242 "connection", gc,
243 "conference", conference,
244 "initiator", initiator,
245 NULL));
246
247 ret = gst_element_set_state(GST_ELEMENT(conference), GST_STATE_PLAYING);
248
249 if (ret == GST_STATE_CHANGE_FAILURE) {
250 purple_conv_present_error(remote_user,
251 purple_connection_get_account(gc),
252 _("Error creating conference."));
253 purple_debug_error("media", "Failed to start conference.\n");
254 g_object_unref(media);
255 return NULL;
256 }
257
258 g_signal_emit(manager, purple_media_manager_signals[INIT_MEDIA], 0,
259 media, gc, remote_user, &signal_ret);
260
261 if (signal_ret == FALSE) {
262 g_object_unref(media);
263 return NULL;
264 }
265
266 manager->priv->medias = g_list_append(manager->priv->medias, media);
267 return media;
268 }
269
270 GList *
271 purple_media_manager_get_media(PurpleMediaManager *manager)
272 {
273 return manager->priv->medias;
274 }
275
276 GList *
277 purple_media_manager_get_media_by_connection(PurpleMediaManager *manager,
278 PurpleConnection *pc)
279 {
280 GList *media = NULL;
281 GList *iter;
282
283 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
284
285 iter = manager->priv->medias;
286 for (; iter; iter = g_list_next(iter)) {
287 if (purple_media_get_connection(iter->data) == pc) {
288 media = g_list_prepend(media, iter->data);
289 }
290 }
291
292 return media;
293 }
294
295 void
296 purple_media_manager_remove_media(PurpleMediaManager *manager,
297 PurpleMedia *media)
298 {
299 GList *list = g_list_find(manager->priv->medias, media);
300 if (list)
301 manager->priv->medias =
302 g_list_delete_link(manager->priv->medias, list);
303 }
304
305 GstElement *
306 purple_media_manager_get_element(PurpleMediaManager *manager,
307 PurpleMediaSessionType type)
308 {
309 GstElement *ret = NULL;
310
311 /* TODO: If src, retrieve current src */
312 /* TODO: Send a signal here to allow for overriding the source/sink */
313
314 if (type & PURPLE_MEDIA_SEND_AUDIO
315 && manager->priv->audio_src != NULL)
316 ret = manager->priv->audio_src->create();
317 else if (type & PURPLE_MEDIA_RECV_AUDIO
318 && manager->priv->audio_sink != NULL)
319 ret = manager->priv->audio_sink->create();
320 else if (type & PURPLE_MEDIA_SEND_VIDEO
321 && manager->priv->video_src != NULL)
322 ret = manager->priv->video_src->create();
323 else if (type & PURPLE_MEDIA_RECV_VIDEO
324 && manager->priv->video_sink != NULL)
325 ret = manager->priv->video_sink->create();
326
327 if (ret == NULL)
328 purple_debug_error("media", "Error creating source or sink\n");
329
330 return ret;
331 }
332
333 PurpleMediaElementInfo *
334 purple_media_manager_get_element_info(PurpleMediaManager *manager,
335 const gchar *id)
336 {
337 GList *iter;
338
339 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
340
341 iter = manager->priv->elements;
342
343 for (; iter; iter = g_list_next(iter)) {
344 PurpleMediaElementInfo *info = iter->data;
345 if (!strcmp(info->id, id))
346 return info;
347 }
348
349 return NULL;
350 }
351
352 gboolean
353 purple_media_manager_register_element(PurpleMediaManager *manager,
354 PurpleMediaElementInfo *info)
355 {
356 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
357 g_return_val_if_fail(info != NULL, FALSE);
358
359 if (purple_media_manager_get_element_info(manager, info->id) != NULL)
360 return FALSE;
361
362 manager->priv->elements =
363 g_list_prepend(manager->priv->elements, info);
364 return TRUE;
365 }
366
367 gboolean
368 purple_media_manager_unregister_element(PurpleMediaManager *manager,
369 const gchar *id)
370 {
371 PurpleMediaElementInfo *info;
372
373 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
374
375 info = purple_media_manager_get_element_info(manager, id);
376
377 if (info == NULL)
378 return FALSE;
379
380 if (manager->priv->audio_src == info)
381 manager->priv->audio_src = NULL;
382 if (manager->priv->audio_sink == info)
383 manager->priv->audio_sink = NULL;
384 if (manager->priv->video_src == info)
385 manager->priv->video_src = NULL;
386 if (manager->priv->video_sink == info)
387 manager->priv->video_sink = NULL;
388
389 manager->priv->elements = g_list_remove(
390 manager->priv->elements, info);
391 return TRUE;
392 }
393
394 gboolean
395 purple_media_manager_set_active_element(PurpleMediaManager *manager,
396 PurpleMediaElementInfo *info)
397 {
398 gboolean ret = FALSE;
399
400 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
401 g_return_val_if_fail(info != NULL, FALSE);
402
403 if (purple_media_manager_get_element_info(manager, info->id) == NULL)
404 purple_media_manager_register_element(manager, info);
405
406 if (info->type & PURPLE_MEDIA_ELEMENT_SRC) {
407 if (info->type & PURPLE_MEDIA_ELEMENT_AUDIO) {
408 manager->priv->audio_src = info;
409 ret = TRUE;
410 }
411 if (info->type & PURPLE_MEDIA_ELEMENT_VIDEO) {
412 manager->priv->video_src = info;
413 ret = TRUE;
414 }
415 }
416 if (info->type & PURPLE_MEDIA_ELEMENT_SINK) {
417 if (info->type & PURPLE_MEDIA_ELEMENT_AUDIO) {
418 manager->priv->audio_sink = info;
419 ret = TRUE;
420 }
421 if (info->type & PURPLE_MEDIA_ELEMENT_VIDEO) {
422 manager->priv->video_sink = info;
423 ret = TRUE;
424 }
425 }
426
427 return ret;
428 }
429
430 PurpleMediaElementInfo *
431 purple_media_manager_get_active_element(PurpleMediaManager *manager,
432 PurpleMediaElementType type)
433 {
434 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL);
435
436 if (type & PURPLE_MEDIA_ELEMENT_SRC) {
437 if (type & PURPLE_MEDIA_ELEMENT_AUDIO)
438 return manager->priv->audio_src;
439 else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
440 return manager->priv->video_src;
441 } else if (type & PURPLE_MEDIA_ELEMENT_SINK) {
442 if (type & PURPLE_MEDIA_ELEMENT_AUDIO)
443 return manager->priv->audio_sink;
444 else if (type & PURPLE_MEDIA_ELEMENT_VIDEO)
445 return manager->priv->video_sink;
446 }
447
448 return NULL;
449 }
450
451 static void
452 window_id_cb(GstBus *bus, GstMessage *msg, PurpleMediaOutputWindow *ow)
453 {
454 if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT ||
455 !gst_structure_has_name(msg->structure,
456 "prepare-xwindow-id"))
457 return;
458
459 if (GST_ELEMENT_PARENT(GST_MESSAGE_SRC(msg)) == ow->sink) {
460 g_signal_handlers_disconnect_matched(bus, G_SIGNAL_MATCH_FUNC
461 | G_SIGNAL_MATCH_DATA, 0, 0, NULL,
462 window_id_cb, ow);
463
464 gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(
465 GST_MESSAGE_SRC(msg)), ow->window_id);
466 }
467 }
468
469 gboolean
470 purple_media_manager_create_output_window(PurpleMediaManager *manager,
471 PurpleMedia *media, const gchar *session_id,
472 const gchar *participant)
473 {
474 GList *iter;
475
476 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
477
478 iter = manager->priv->output_windows;
479 for(; iter; iter = g_list_next(iter)) {
480 PurpleMediaOutputWindow *ow = iter->data;
481
482 if (ow->sink == NULL && ow->media == media &&
483 ((participant != NULL &&
484 ow->participant != NULL &&
485 !strcmp(participant, ow->participant)) ||
486 (participant == ow->participant)) &&
487 !strcmp(session_id, ow->session_id)) {
488 GstBus *bus;
489 GstElement *queue;
490 GstElement *tee = purple_media_get_tee(media,
491 session_id, participant);
492
493 if (tee == NULL)
494 continue;
495
496 queue = gst_element_factory_make(
497 "queue", NULL);
498 ow->sink = purple_media_manager_get_element(
499 manager, PURPLE_MEDIA_RECV_VIDEO);
500
501 if (participant == NULL) {
502 /* aka this is a preview sink */
503 GObjectClass *klass =
504 G_OBJECT_GET_CLASS(ow->sink);
505 if (g_object_class_find_property(klass,
506 "sync"))
507 g_object_set(G_OBJECT(ow->sink),
508 "sync", "FALSE", NULL);
509 if (g_object_class_find_property(klass,
510 "async"))
511 g_object_set(G_OBJECT(ow->sink),
512 "async", FALSE, NULL);
513 }
514
515 gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee)),
516 queue, ow->sink, NULL);
517
518 bus = gst_pipeline_get_bus(GST_PIPELINE(
519 manager->priv->pipeline));
520 g_signal_connect(bus, "sync-message::element",
521 G_CALLBACK(window_id_cb), ow);
522 gst_object_unref(bus);
523
524 gst_element_sync_state_with_parent(ow->sink);
525 gst_element_link(queue, ow->sink);
526 gst_element_sync_state_with_parent(queue);
527 gst_element_link(tee, queue);
528 }
529 }
530 return TRUE;
531 }
532
533 gulong
534 purple_media_manager_set_output_window(PurpleMediaManager *manager,
535 PurpleMedia *media, const gchar *session_id,
536 const gchar *participant, gulong window_id)
537 {
538 PurpleMediaOutputWindow *output_window;
539
540 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
541 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
542
543 output_window = g_new0(PurpleMediaOutputWindow, 1);
544 output_window->id = manager->priv->next_output_window_id++;
545 output_window->media = media;
546 output_window->session_id = g_strdup(session_id);
547 output_window->participant = g_strdup(participant);
548 output_window->window_id = window_id;
549
550 manager->priv->output_windows = g_list_prepend(
551 manager->priv->output_windows, output_window);
552
553 if (purple_media_get_tee(media, session_id, participant) != NULL)
554 purple_media_manager_create_output_window(manager,
555 media, session_id, participant);
556
557 return output_window->id;
558 }
559
560 gboolean
561 purple_media_manager_remove_output_window(PurpleMediaManager *manager,
562 gulong output_window_id)
563 {
564 PurpleMediaOutputWindow *output_window = NULL;
565 GList *iter;
566
567 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE);
568
569 iter = manager->priv->output_windows;
570 for (; iter; iter = g_list_next(iter)) {
571 PurpleMediaOutputWindow *ow = iter->data;
572 if (ow->id == output_window_id) {
573 manager->priv->output_windows = g_list_delete_link(
574 manager->priv->output_windows, iter);
575 output_window = ow;
576 break;
577 }
578 }
579
580 if (output_window == NULL)
581 return FALSE;
582
583 if (output_window->sink != NULL) {
584 GstPad *pad = gst_element_get_static_pad(
585 output_window->sink, "sink");
586 GstPad *peer = gst_pad_get_peer(pad);
587 GstElement *queue = GST_ELEMENT_PARENT(peer);
588 gst_object_unref(pad);
589 pad = gst_element_get_static_pad(queue, "sink");
590 peer = gst_pad_get_peer(pad);
591 gst_object_unref(pad);
592 gst_element_release_request_pad(GST_ELEMENT_PARENT(peer), peer);
593 gst_element_set_locked_state(queue, TRUE);
594 gst_element_set_state(queue, GST_STATE_NULL);
595 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(queue)), queue);
596 gst_element_set_locked_state(output_window->sink, TRUE);
597 gst_element_set_state(output_window->sink, GST_STATE_NULL);
598 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(output_window->sink)),
599 output_window->sink);
600 }
601
602 g_free(output_window->session_id);
603 g_free(output_window->participant);
604 g_free(output_window);
605
606 return TRUE;
607 }
608
609 void
610 purple_media_manager_remove_output_windows(PurpleMediaManager *manager,
611 PurpleMedia *media, const gchar *session_id,
612 const gchar *participant)
613 {
614 GList *iter;
615
616 g_return_if_fail(PURPLE_IS_MEDIA(media));
617
618 iter = manager->priv->output_windows;
619
620 for (; iter;) {
621 PurpleMediaOutputWindow *ow = iter->data;
622 iter = g_list_next(iter);
623
624 if (media == ow->media &&
625 ((session_id != NULL && ow->session_id != NULL &&
626 !strcmp(session_id, ow->session_id)) ||
627 (session_id == ow->session_id)) &&
628 ((participant != NULL && ow->participant != NULL &&
629 !strcmp(participant, ow->participant)) ||
630 (participant == ow->participant)))
631 purple_media_manager_remove_output_window(
632 manager, ow->id);
633 }
634 }
635
636 #endif /* USE_VV */