Mercurial > pidgin.yaz
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 |