3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "mediamanager.h"
30 #include "media-gst.h"
31 #include <media/backend-fs2.h>
32 #endif /* USE_GSTREAMER */
35 #include <farstream/fs-element-added-notifier.h>
36 #include <gst/video/videooverlay.h>
37 #ifdef HAVE_MEDIA_APPLICATION
38 #include <gst/app/app.h>
42 /** @copydoc _PurpleMediaOutputWindow */
43 typedef struct _PurpleMediaOutputWindow PurpleMediaOutputWindow
;
44 /** @copydoc _PurpleMediaManagerPrivate */
45 typedef struct _PurpleMediaElementInfoPrivate PurpleMediaElementInfoPrivate
;
47 struct _PurpleMediaOutputWindow
65 PurpleMediaCaps ui_caps
;
67 GList
*private_medias
;
69 GList
*output_windows
;
70 gulong next_output_window_id
;
74 PurpleMediaElementInfo
*video_src
;
75 PurpleMediaElementInfo
*video_sink
;
76 PurpleMediaElementInfo
*audio_src
;
77 PurpleMediaElementInfo
*audio_sink
;
79 #if GST_CHECK_VERSION(1, 4, 0)
80 GstDeviceMonitor
*device_monitor
;
81 #endif /* GST_CHECK_VERSION(1, 4, 0) */
83 #ifdef HAVE_MEDIA_APPLICATION
84 /* Application data streams */
85 GList
*appdata_info
; /* holds PurpleMediaAppDataInfo */
87 guint appdata_cb_token
; /* last used read/write callback token */
90 } PurpleMediaManagerPrivate
;
95 * The media manager's data.
97 struct _PurpleMediaManager
102 PurpleMediaManagerPrivate
*priv
;
105 #ifdef HAVE_MEDIA_APPLICATION
111 PurpleMediaAppDataCallbacks callbacks
;
113 GDestroyNotify notify
;
117 GstSample
*current_sample
;
121 guint writable_cb_token
;
122 guint readable_cb_token
;
123 guint writable_timer_id
;
124 guint readable_timer_id
;
126 } PurpleMediaAppDataInfo
;
130 static void purple_media_manager_finalize (GObject
*object
);
131 #ifdef HAVE_MEDIA_APPLICATION
132 static void free_appdata_info_locked (PurpleMediaAppDataInfo
*info
);
134 static void purple_media_manager_init_device_monitor(PurpleMediaManager
*manager
);
135 static void purple_media_manager_register_static_elements(PurpleMediaManager
*manager
);
145 static guint purple_media_manager_signals
[LAST_SIGNAL
] = {0};
147 G_DEFINE_TYPE_WITH_PRIVATE(PurpleMediaManager
, purple_media_manager
,
151 purple_media_manager_get_type()
159 purple_media_manager_class_init (PurpleMediaManagerClass
*klass
)
161 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
163 gobject_class
->finalize
= purple_media_manager_finalize
;
165 purple_media_manager_signals
[INIT_MEDIA
] = g_signal_new ("init-media",
166 G_TYPE_FROM_CLASS (klass
),
169 G_TYPE_BOOLEAN
, 3, PURPLE_TYPE_MEDIA
,
170 G_TYPE_POINTER
, G_TYPE_STRING
);
172 purple_media_manager_signals
[INIT_PRIVATE_MEDIA
] =
173 g_signal_new ("init-private-media",
174 G_TYPE_FROM_CLASS (klass
),
177 G_TYPE_BOOLEAN
, 3, PURPLE_TYPE_MEDIA
,
178 G_TYPE_POINTER
, G_TYPE_STRING
);
180 purple_media_manager_signals
[UI_CAPS_CHANGED
] = g_signal_new ("ui-caps-changed",
181 G_TYPE_FROM_CLASS (klass
),
184 G_TYPE_NONE
, 2, PURPLE_MEDIA_TYPE_CAPS
,
185 PURPLE_MEDIA_TYPE_CAPS
);
187 purple_media_manager_signals
[ELEMENTS_CHANGED
] =
188 g_signal_new("elements-changed",
189 G_TYPE_FROM_CLASS(klass
),
190 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
196 purple_media_manager_init (PurpleMediaManager
*media
)
200 media
->priv
= purple_media_manager_get_instance_private(media
);
201 media
->priv
->medias
= NULL
;
202 media
->priv
->private_medias
= NULL
;
203 media
->priv
->next_output_window_id
= 1;
204 media
->priv
->backend_type
= PURPLE_TYPE_MEDIA_BACKEND_FS2
;
205 #ifdef HAVE_MEDIA_APPLICATION
206 media
->priv
->appdata_info
= NULL
;
207 g_mutex_init (&media
->priv
->appdata_mutex
);
209 if (gst_init_check(NULL
, NULL
, &error
)) {
210 purple_media_manager_register_static_elements(media
);
211 purple_media_manager_init_device_monitor(media
);
213 purple_debug_error("mediamanager",
214 "GStreamer failed to initialize: %s.",
215 error
? error
->message
: "");
221 purple_prefs_add_none("/purple/media");
222 purple_prefs_add_none("/purple/media/audio");
223 purple_prefs_add_int("/purple/media/audio/silence_threshold", 5);
224 purple_prefs_add_none("/purple/media/audio/volume");
225 purple_prefs_add_int("/purple/media/audio/volume/input", 10);
226 purple_prefs_add_int("/purple/media/audio/volume/output", 10);
230 purple_media_manager_finalize (GObject
*media
)
232 PurpleMediaManagerPrivate
*priv
=
233 purple_media_manager_get_instance_private(
234 PURPLE_MEDIA_MANAGER(media
));
236 for (; priv
->medias
; priv
->medias
=
237 g_list_delete_link(priv
->medias
, priv
->medias
)) {
238 g_object_unref(priv
->medias
->data
);
240 for (; priv
->private_medias
; priv
->private_medias
=
241 g_list_delete_link(priv
->private_medias
, priv
->private_medias
)) {
242 g_object_unref(priv
->private_medias
->data
);
244 for (; priv
->elements
; priv
->elements
=
245 g_list_delete_link(priv
->elements
, priv
->elements
)) {
246 g_object_unref(priv
->elements
->data
);
248 if (priv
->video_caps
)
249 gst_caps_unref(priv
->video_caps
);
250 #ifdef HAVE_MEDIA_APPLICATION
251 if (priv
->appdata_info
)
252 g_list_free_full (priv
->appdata_info
,
253 (GDestroyNotify
) free_appdata_info_locked
);
254 g_mutex_clear (&priv
->appdata_mutex
);
256 #if GST_CHECK_VERSION(1, 4, 0)
257 if (priv
->device_monitor
) {
258 gst_device_monitor_stop(priv
->device_monitor
);
259 g_object_unref(priv
->device_monitor
);
261 #endif /* GST_CHECK_VERSION(1, 4, 0) */
263 G_OBJECT_CLASS(purple_media_manager_parent_class
)->finalize(media
);
268 purple_media_manager_get()
271 static PurpleMediaManager
*manager
= NULL
;
274 manager
= PURPLE_MEDIA_MANAGER(g_object_new(purple_media_manager_get_type(), NULL
));
283 pipeline_bus_call(GstBus
*bus
, GstMessage
*msg
, PurpleMediaManager
*manager
)
285 switch(GST_MESSAGE_TYPE(msg
)) {
286 case GST_MESSAGE_EOS
:
287 purple_debug_info("mediamanager", "End of Stream\n");
289 case GST_MESSAGE_ERROR
: {
293 gst_message_parse_error(msg
, &err
, &debug
);
295 purple_debug_error("mediamanager",
296 "gst pipeline error: %s\n",
301 purple_debug_error("mediamanager",
302 "Debug details: %s\n", debug
);
316 purple_media_manager_get_pipeline(PurpleMediaManager
*manager
)
318 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
320 if (manager
->priv
->pipeline
== NULL
) {
321 FsElementAddedNotifier
*notifier
;
326 manager
->priv
->pipeline
= gst_pipeline_new(NULL
);
328 bus
= gst_pipeline_get_bus(
329 GST_PIPELINE(manager
->priv
->pipeline
));
330 gst_bus_add_signal_watch(GST_BUS(bus
));
331 g_signal_connect(G_OBJECT(bus
), "message",
332 G_CALLBACK(pipeline_bus_call
), manager
);
333 gst_bus_set_sync_handler(bus
, gst_bus_sync_signal_handler
, NULL
, NULL
);
334 gst_object_unref(bus
);
336 filename
= g_build_filename(purple_config_dir(),
337 "fs-element.conf", NULL
);
338 keyfile
= g_key_file_new();
339 if (!g_key_file_load_from_file(keyfile
, filename
,
340 G_KEY_FILE_NONE
, &err
)) {
342 purple_debug_info("mediamanager",
344 "fs-element.conf: %s\n",
347 purple_debug_error("mediamanager",
349 "fs-element.conf: %s\n",
355 /* Hack to make alsasrc stop messing up audio timestamps */
356 if (!g_key_file_has_key(keyfile
,
357 "alsasrc", "slave-method", NULL
)) {
358 g_key_file_set_integer(keyfile
,
359 "alsasrc", "slave-method", 2);
362 notifier
= fs_element_added_notifier_new();
363 fs_element_added_notifier_add(notifier
,
364 GST_BIN(manager
->priv
->pipeline
));
365 fs_element_added_notifier_set_properties_from_keyfile(
368 gst_element_set_state(manager
->priv
->pipeline
,
372 return manager
->priv
->pipeline
;
377 create_media(PurpleMediaManager
*manager
,
378 PurpleAccount
*account
,
379 const char *conference_type
,
380 const char *remote_user
,
388 media
= PURPLE_MEDIA(g_object_new(purple_media_get_type(),
391 "conference-type", conference_type
,
392 "initiator", initiator
,
395 signal_id
= private ?
396 purple_media_manager_signals
[INIT_PRIVATE_MEDIA
] :
397 purple_media_manager_signals
[INIT_MEDIA
];
399 if (g_signal_has_handler_pending(manager
, signal_id
, 0, FALSE
)) {
402 g_signal_emit(manager
, signal_id
, 0, media
, account
, remote_user
,
404 if (signal_ret
== FALSE
) {
405 g_object_unref(media
);
411 manager
->priv
->private_medias
= g_list_append(
412 manager
->priv
->private_medias
, media
);
414 manager
->priv
->medias
= g_list_append(manager
->priv
->medias
, media
);
422 get_media(PurpleMediaManager
*manager
, gboolean
private)
426 return manager
->priv
->private_medias
;
428 return manager
->priv
->medias
;
435 get_media_by_account(PurpleMediaManager
*manager
,
436 PurpleAccount
*account
, gboolean
private)
442 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
445 iter
= manager
->priv
->private_medias
;
447 iter
= manager
->priv
->medias
;
448 for (; iter
; iter
= g_list_next(iter
)) {
449 if (purple_media_get_account(iter
->data
) == account
) {
450 media
= g_list_prepend(media
, iter
->data
);
461 purple_media_manager_remove_media(PurpleMediaManager
*manager
, PurpleMedia
*media
)
465 GList
**medias
= NULL
;
467 g_return_if_fail(manager
!= NULL
);
469 if ((list
= g_list_find(manager
->priv
->medias
, media
))) {
470 medias
= &manager
->priv
->medias
;
471 } else if ((list
= g_list_find(manager
->priv
->private_medias
, media
))) {
472 medias
= &manager
->priv
->private_medias
;
476 *medias
= g_list_delete_link(*medias
, list
);
478 #ifdef HAVE_MEDIA_APPLICATION
479 g_mutex_lock (&manager
->priv
->appdata_mutex
);
480 list
= manager
->priv
->appdata_info
;
482 PurpleMediaAppDataInfo
*info
= list
->data
;
483 GList
*next
= list
->next
;
485 if (info
->media
== media
) {
486 manager
->priv
->appdata_info
= g_list_delete_link (
487 manager
->priv
->appdata_info
, list
);
488 free_appdata_info_locked (info
);
493 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
500 purple_media_manager_create_media(PurpleMediaManager
*manager
,
501 PurpleAccount
*account
,
502 const char *conference_type
,
503 const char *remote_user
,
506 return create_media (manager
, account
, conference_type
,
507 remote_user
, initiator
, FALSE
);
511 purple_media_manager_get_media(PurpleMediaManager
*manager
)
513 return get_media (manager
, FALSE
);
517 purple_media_manager_get_media_by_account(PurpleMediaManager
*manager
,
518 PurpleAccount
*account
)
520 return get_media_by_account (manager
, account
, FALSE
);
524 purple_media_manager_create_private_media(PurpleMediaManager
*manager
,
525 PurpleAccount
*account
,
526 const char *conference_type
,
527 const char *remote_user
,
530 return create_media (manager
, account
, conference_type
,
531 remote_user
, initiator
, TRUE
);
535 purple_media_manager_get_private_media(PurpleMediaManager
*manager
)
537 return get_media (manager
, TRUE
);
541 purple_media_manager_get_private_media_by_account(PurpleMediaManager
*manager
,
542 PurpleAccount
*account
)
544 return get_media_by_account (manager
, account
, TRUE
);
547 #ifdef HAVE_MEDIA_APPLICATION
549 free_appdata_info_locked (PurpleMediaAppDataInfo
*info
)
551 GstAppSrcCallbacks null_src_cb
= { NULL
, NULL
, NULL
, { NULL
} };
552 GstAppSinkCallbacks null_sink_cb
= { NULL
, NULL
, NULL
, { NULL
} };
555 info
->notify (info
->user_data
);
559 /* Will call appsrc_destroyed. */
560 gst_app_src_set_callbacks (info
->appsrc
, &null_src_cb
,
564 /* Will call appsink_destroyed. */
565 gst_app_sink_set_callbacks (info
->appsink
, &null_sink_cb
,
569 /* Make sure no other thread is using the structure */
570 g_free (info
->session_id
);
571 g_free (info
->participant
);
573 /* This lets the potential read or write callbacks waiting for appdata_mutex
574 * know the info structure has been destroyed. */
575 info
->readable_cb_token
= 0;
576 info
->writable_cb_token
= 0;
578 if (info
->readable_timer_id
) {
579 g_source_remove (info
->readable_timer_id
);
580 info
->readable_timer_id
= 0;
583 if (info
->writable_timer_id
) {
584 g_source_remove (info
->writable_timer_id
);
585 info
->writable_timer_id
= 0;
588 if (info
->current_sample
)
589 gst_sample_unref (info
->current_sample
);
590 info
->current_sample
= NULL
;
592 /* Unblock any reading thread before destroying the GCond */
593 g_cond_broadcast (&info
->readable_cond
);
595 g_cond_clear (&info
->readable_cond
);
597 g_slice_free (PurpleMediaAppDataInfo
, info
);
601 * Get an app data info struct associated with a session and lock the mutex
602 * We don't want to return an info struct and unlock then it gets destroyed
603 * so we need to return it with the lock still taken
605 static PurpleMediaAppDataInfo
*
606 get_app_data_info_and_lock (PurpleMediaManager
*manager
,
607 PurpleMedia
*media
, const gchar
*session_id
, const gchar
*participant
)
611 g_mutex_lock (&manager
->priv
->appdata_mutex
);
612 for (i
= manager
->priv
->appdata_info
; i
; i
= i
->next
) {
613 PurpleMediaAppDataInfo
*info
= i
->data
;
615 if (info
->media
== media
&&
616 purple_strequal (info
->session_id
, session_id
) &&
617 (participant
== NULL
||
618 purple_strequal (info
->participant
, participant
))) {
627 * Get an app data info struct associated with a session and lock the mutex
628 * if it doesn't exist, we create it.
630 static PurpleMediaAppDataInfo
*
631 ensure_app_data_info_and_lock (PurpleMediaManager
*manager
, PurpleMedia
*media
,
632 const gchar
*session_id
, const gchar
*participant
)
634 PurpleMediaAppDataInfo
* info
= get_app_data_info_and_lock (manager
, media
,
635 session_id
, participant
);
638 info
= g_slice_new0 (PurpleMediaAppDataInfo
);
640 g_weak_ref_init (&info
->media_ref
, media
);
641 info
->session_id
= g_strdup (session_id
);
642 info
->participant
= g_strdup (participant
);
643 g_cond_init (&info
->readable_cond
);
644 manager
->priv
->appdata_info
= g_list_prepend (
645 manager
->priv
->appdata_info
, info
);
655 request_pad_unlinked_cb(GstPad
*pad
, GstPad
*peer
, gpointer user_data
)
657 GstElement
*parent
= GST_ELEMENT_PARENT(pad
);
659 GValue tmp
= G_VALUE_INIT
;
660 GstPad
*remaining_pad
;
661 GstIteratorResult result
;
663 gst_element_release_request_pad(parent
, pad
);
665 iter
= gst_element_iterate_src_pads(parent
);
667 result
= gst_iterator_next(iter
, &tmp
);
669 if (result
== GST_ITERATOR_DONE
) {
670 gst_element_set_locked_state(parent
, TRUE
);
671 gst_element_set_state(parent
, GST_STATE_NULL
);
672 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(parent
)), parent
);
673 } else if (result
== GST_ITERATOR_OK
) {
674 remaining_pad
= g_value_get_object(&tmp
);
676 gst_object_unref(remaining_pad
);
679 gst_iterator_free(iter
);
683 nonunique_src_unlinked_cb(GstPad
*pad
, GstPad
*peer
, gpointer user_data
)
685 GstElement
*element
= GST_ELEMENT_PARENT(pad
);
686 gst_element_set_locked_state(element
, TRUE
);
687 gst_element_set_state(element
, GST_STATE_NULL
);
688 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(element
)), element
);
692 purple_media_manager_set_video_caps(PurpleMediaManager
*manager
, GstCaps
*caps
)
694 if (manager
->priv
->video_caps
)
695 gst_caps_unref(manager
->priv
->video_caps
);
697 manager
->priv
->video_caps
= caps
;
699 if (manager
->priv
->pipeline
&& manager
->priv
->video_src
) {
700 gchar
*id
= purple_media_element_info_get_id(manager
->priv
->video_src
);
701 GstElement
*src
= gst_bin_get_by_name(GST_BIN(manager
->priv
->pipeline
), id
);
704 GstElement
*capsfilter
= gst_bin_get_by_name(GST_BIN(src
), "protocol_video_caps");
706 g_object_set(G_OBJECT(capsfilter
), "caps", caps
, NULL
);
707 gst_object_unref (capsfilter
);
709 gst_object_unref (src
);
717 purple_media_manager_get_video_caps(PurpleMediaManager
*manager
)
719 if (manager
->priv
->video_caps
== NULL
)
720 manager
->priv
->video_caps
= gst_caps_from_string("video/x-raw,"
721 "width=[250,352], height=[200,288], framerate=[1/1,20/1]");
722 return manager
->priv
->video_caps
;
726 #ifdef HAVE_MEDIA_APPLICATION
728 * Calls the appdata writable callback from the main thread.
729 * This needs to grab the appdata lock and make sure it didn't get destroyed
730 * before calling the callback.
733 appsrc_writable (gpointer user_data
)
735 PurpleMediaManager
*manager
= purple_media_manager_get ();
736 PurpleMediaAppDataInfo
*info
= user_data
;
737 void (*writable_cb
) (PurpleMediaManager
*manager
, PurpleMedia
*media
,
738 const gchar
*session_id
, const gchar
*participant
, gboolean writable
,
745 guint
*cb_token_ptr
= &info
->writable_cb_token
;
746 guint cb_token
= *cb_token_ptr
;
749 g_mutex_lock (&manager
->priv
->appdata_mutex
);
750 if (cb_token
== 0 || cb_token
!= *cb_token_ptr
) {
751 /* In case info was freed while we were waiting for the mutex to unlock
752 * we still have a pointer to the cb_token which should still be
753 * accessible since it's in the Glib slice allocator. It gets set to 0
754 * just after the timeout is canceled which happens also before the
755 * AppDataInfo is freed, so even if that memory slice gets reused, the
756 * cb_token would be different from its previous value (unless
757 * extremely unlucky). So checking if the value for the cb_token changed
758 * should be enough to prevent any kind of race condition in which the
759 * media/AppDataInfo gets destroyed in one thread while the timeout was
760 * triggered and is waiting on the mutex to get unlocked in this thread
762 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
765 writable_cb
= info
->callbacks
.writable
;
766 media
= g_weak_ref_get (&info
->media_ref
);
767 session_id
= g_strdup (info
->session_id
);
768 participant
= g_strdup (info
->participant
);
769 writable
= info
->writable
&& info
->connected
;
770 cb_data
= info
->user_data
;
772 info
->writable_cb_token
= 0;
773 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
776 if (writable_cb
&& media
)
777 writable_cb (manager
, media
, session_id
, participant
, writable
,
780 g_object_unref (media
);
782 g_free (participant
);
788 * Schedule a writable callback to be called from the main thread.
789 * We need to do this because need-data/enough-data signals from appsrc
790 * will come from the streaming thread and we need to create
791 * a source that we attach to the main context but we can't use
792 * g_main_context_invoke since we need to be able to cancel the source if the
793 * media gets destroyed.
794 * We use a timeout source instead of idle source, so the callback gets a higher
798 call_appsrc_writable_locked (PurpleMediaAppDataInfo
*info
)
800 PurpleMediaManager
*manager
= purple_media_manager_get ();
802 /* We already have a writable callback scheduled, don't create another one */
803 if (info
->writable_cb_token
|| info
->callbacks
.writable
== NULL
)
806 /* We can't use writable_timer_id as a token, because the timeout is added
807 * into libpurple's main event loop, which runs in a different thread than
808 * from where call_appsrc_writable_locked() was called. Consequently, the
809 * callback may run even before g_timeout_add() returns the timer ID
811 info
->writable_cb_token
= ++manager
->priv
->appdata_cb_token
;
812 info
->writable_timer_id
= g_timeout_add (0, appsrc_writable
, info
);
816 appsrc_need_data (GstAppSrc
*appsrc
, guint length
, gpointer user_data
)
818 PurpleMediaAppDataInfo
*info
= user_data
;
819 PurpleMediaManager
*manager
= purple_media_manager_get ();
821 g_mutex_lock (&manager
->priv
->appdata_mutex
);
822 if (!info
->writable
) {
823 info
->writable
= TRUE
;
824 /* Only signal writable if we also established a connection */
826 call_appsrc_writable_locked (info
);
828 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
832 appsrc_enough_data (GstAppSrc
*appsrc
, gpointer user_data
)
834 PurpleMediaAppDataInfo
*info
= user_data
;
835 PurpleMediaManager
*manager
= purple_media_manager_get ();
837 g_mutex_lock (&manager
->priv
->appdata_mutex
);
838 if (info
->writable
) {
839 info
->writable
= FALSE
;
840 call_appsrc_writable_locked (info
);
842 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
846 appsrc_seek_data (GstAppSrc
*appsrc
, guint64 offset
, gpointer user_data
)
852 appsrc_destroyed (PurpleMediaAppDataInfo
*info
)
854 PurpleMediaManager
*manager
;
857 /* PurpleMediaAppDataInfo is being freed. Return at once. */
861 manager
= purple_media_manager_get ();
863 g_mutex_lock (&manager
->priv
->appdata_mutex
);
865 if (info
->writable
) {
866 info
->writable
= FALSE
;
867 call_appsrc_writable_locked (info
);
869 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
873 media_established_cb (PurpleMedia
*media
,const gchar
*session_id
,
874 const gchar
*participant
, PurpleMediaCandidate
*local_candidate
,
875 PurpleMediaCandidate
*remote_candidate
, PurpleMediaAppDataInfo
*info
)
877 PurpleMediaManager
*manager
= purple_media_manager_get ();
879 g_mutex_lock (&manager
->priv
->appdata_mutex
);
880 info
->connected
= TRUE
;
881 /* We established the connection, if we were writable, then we need to
884 call_appsrc_writable_locked (info
);
885 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
889 create_send_appsrc(PurpleMediaElementInfo
*element_info
, PurpleMedia
*media
,
890 const gchar
*session_id
, const gchar
*participant
)
892 PurpleMediaManager
*manager
= purple_media_manager_get ();
893 PurpleMediaAppDataInfo
* info
= ensure_app_data_info_and_lock (manager
,
894 media
, session_id
, participant
);
895 GstElement
*appsrc
= (GstElement
*)info
->appsrc
;
897 if (appsrc
== NULL
) {
898 GstAppSrcCallbacks callbacks
= {appsrc_need_data
, appsrc_enough_data
,
899 appsrc_seek_data
, {NULL
}};
900 GstCaps
*caps
= gst_caps_new_empty_simple ("application/octet-stream");
902 appsrc
= gst_element_factory_make("appsrc", NULL
);
904 info
->appsrc
= (GstAppSrc
*)appsrc
;
906 gst_app_src_set_caps (info
->appsrc
, caps
);
907 gst_app_src_set_callbacks (info
->appsrc
,
908 &callbacks
, info
, (GDestroyNotify
) appsrc_destroyed
);
909 g_signal_connect (media
, "candidate-pair-established",
910 (GCallback
) media_established_cb
, info
);
911 gst_caps_unref (caps
);
914 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
919 appsink_eos (GstAppSink
*appsink
, gpointer user_data
)
924 appsink_new_preroll (GstAppSink
*appsink
, gpointer user_data
)
930 appsink_readable (gpointer user_data
)
932 PurpleMediaManager
*manager
= purple_media_manager_get ();
933 PurpleMediaAppDataInfo
*info
= user_data
;
934 void (*readable_cb
) (PurpleMediaManager
*manager
, PurpleMedia
*media
,
935 const gchar
*session_id
, const gchar
*participant
, gpointer user_data
);
940 guint
*cb_token_ptr
= &info
->readable_cb_token
;
941 guint cb_token
= *cb_token_ptr
;
942 gboolean run_again
= FALSE
;
944 g_mutex_lock (&manager
->priv
->appdata_mutex
);
945 if (cb_token
== 0 || cb_token
!= *cb_token_ptr
) {
946 /* Avoided a race condition (see writable callback) */
947 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
951 if (info
->callbacks
.readable
&&
952 (info
->num_samples
> 0 || info
->current_sample
!= NULL
)) {
953 readable_cb
= info
->callbacks
.readable
;
954 media
= g_weak_ref_get (&info
->media_ref
);
955 session_id
= g_strdup (info
->session_id
);
956 participant
= g_strdup (info
->participant
);
957 cb_data
= info
->user_data
;
958 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
961 readable_cb (manager
, media
, session_id
, participant
, cb_data
);
963 g_mutex_lock (&manager
->priv
->appdata_mutex
);
964 g_object_unref (media
);
966 g_free (participant
);
967 if (cb_token
== 0 || cb_token
!= *cb_token_ptr
) {
968 /* We got cancelled */
969 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
974 /* Do we still have samples? Schedule appsink_readable again. We break here
975 * so that other events get a chance to be processed too. */
976 if (info
->num_samples
> 0 || info
->current_sample
!= NULL
) {
979 info
->readable_cb_token
= 0;
982 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
987 call_appsink_readable_locked (PurpleMediaAppDataInfo
*info
)
989 PurpleMediaManager
*manager
= purple_media_manager_get ();
991 /* We must signal that a new sample has arrived to release blocking reads */
992 g_cond_broadcast (&info
->readable_cond
);
994 /* We already have a writable callback scheduled, don't create another one */
995 if (info
->readable_cb_token
|| info
->callbacks
.readable
== NULL
)
998 info
->readable_cb_token
= ++manager
->priv
->appdata_cb_token
;
999 info
->readable_timer_id
= g_timeout_add (0, appsink_readable
, info
);
1002 static GstFlowReturn
1003 appsink_new_sample (GstAppSink
*appsink
, gpointer user_data
)
1005 PurpleMediaManager
*manager
= purple_media_manager_get ();
1006 PurpleMediaAppDataInfo
*info
= user_data
;
1008 g_mutex_lock (&manager
->priv
->appdata_mutex
);
1009 info
->num_samples
++;
1010 call_appsink_readable_locked (info
);
1011 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1017 appsink_destroyed (PurpleMediaAppDataInfo
*info
)
1019 PurpleMediaManager
*manager
;
1022 /* PurpleMediaAppDataInfo is being freed. Return at once. */
1026 manager
= purple_media_manager_get ();
1028 g_mutex_lock (&manager
->priv
->appdata_mutex
);
1029 info
->appsink
= NULL
;
1030 info
->num_samples
= 0;
1031 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1035 create_recv_appsink(PurpleMediaElementInfo
*element_info
, PurpleMedia
*media
,
1036 const gchar
*session_id
, const gchar
*participant
)
1038 PurpleMediaManager
*manager
= purple_media_manager_get ();
1039 PurpleMediaAppDataInfo
* info
= ensure_app_data_info_and_lock (manager
,
1040 media
, session_id
, participant
);
1041 GstElement
*appsink
= (GstElement
*)info
->appsink
;
1043 if (appsink
== NULL
) {
1044 GstAppSinkCallbacks callbacks
= {appsink_eos
, appsink_new_preroll
,
1045 appsink_new_sample
, {NULL
}};
1046 GstCaps
*caps
= gst_caps_new_empty_simple ("application/octet-stream");
1048 appsink
= gst_element_factory_make("appsink", NULL
);
1050 info
->appsink
= (GstAppSink
*)appsink
;
1052 gst_app_sink_set_caps (info
->appsink
, caps
);
1053 gst_app_sink_set_callbacks (info
->appsink
,
1054 &callbacks
, info
, (GDestroyNotify
) appsink_destroyed
);
1055 gst_caps_unref (caps
);
1059 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1062 #endif /* HAVE_MEDIA_APPLICATION */
1065 static PurpleMediaElementInfo
*
1066 get_send_application_element_info ()
1068 static PurpleMediaElementInfo
*info
= NULL
;
1070 #ifdef HAVE_MEDIA_APPLICATION
1072 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1073 "id", "pidginappsrc",
1074 "name", "Pidgin Application Source",
1075 "type", PURPLE_MEDIA_ELEMENT_APPLICATION
1076 | PURPLE_MEDIA_ELEMENT_SRC
1077 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
1078 "create-cb", create_send_appsrc
, NULL
);
1085 static PurpleMediaElementInfo
*
1086 get_recv_application_element_info ()
1088 static PurpleMediaElementInfo
*info
= NULL
;
1090 #ifdef HAVE_MEDIA_APPLICATION
1092 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1093 "id", "pidginappsink",
1094 "name", "Pidgin Application Sink",
1095 "type", PURPLE_MEDIA_ELEMENT_APPLICATION
1096 | PURPLE_MEDIA_ELEMENT_SINK
1097 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
1098 "create-cb", create_recv_appsink
, NULL
);
1106 purple_media_manager_get_element(PurpleMediaManager
*manager
,
1107 PurpleMediaSessionType type
, PurpleMedia
*media
,
1108 const gchar
*session_id
, const gchar
*participant
)
1110 GstElement
*ret
= NULL
;
1111 PurpleMediaElementInfo
*info
= NULL
;
1112 PurpleMediaElementType element_type
;
1114 if (type
& PURPLE_MEDIA_SEND_AUDIO
)
1115 info
= manager
->priv
->audio_src
;
1116 else if (type
& PURPLE_MEDIA_RECV_AUDIO
)
1117 info
= manager
->priv
->audio_sink
;
1118 else if (type
& PURPLE_MEDIA_SEND_VIDEO
)
1119 info
= manager
->priv
->video_src
;
1120 else if (type
& PURPLE_MEDIA_RECV_VIDEO
)
1121 info
= manager
->priv
->video_sink
;
1122 else if (type
& PURPLE_MEDIA_SEND_APPLICATION
)
1123 info
= get_send_application_element_info ();
1124 else if (type
& PURPLE_MEDIA_RECV_APPLICATION
)
1125 info
= get_recv_application_element_info ();
1130 element_type
= purple_media_element_info_get_element_type(info
);
1132 if (element_type
& PURPLE_MEDIA_ELEMENT_UNIQUE
&&
1133 element_type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1137 gchar
*id
= purple_media_element_info_get_id(info
);
1139 ret
= gst_bin_get_by_name(GST_BIN(
1140 purple_media_manager_get_pipeline(
1144 GstElement
*bin
, *fakesink
;
1145 ret
= purple_media_element_info_call_create(info
,
1146 media
, session_id
, participant
);
1147 bin
= gst_bin_new(id
);
1148 tee
= gst_element_factory_make("tee", "tee");
1149 gst_bin_add_many(GST_BIN(bin
), ret
, tee
, NULL
);
1151 if (type
& PURPLE_MEDIA_SEND_VIDEO
) {
1152 GstElement
*videoscale
;
1153 GstElement
*capsfilter
;
1155 videoscale
= gst_element_factory_make("videoscale", NULL
);
1156 capsfilter
= gst_element_factory_make("capsfilter", "protocol_video_caps");
1158 g_object_set(G_OBJECT(capsfilter
),
1159 "caps", purple_media_manager_get_video_caps(manager
), NULL
);
1161 gst_bin_add_many(GST_BIN(bin
), videoscale
, capsfilter
, NULL
);
1162 gst_element_link_many(ret
, videoscale
, capsfilter
, tee
, NULL
);
1164 gst_element_link(ret
, tee
);
1167 * This shouldn't be necessary, but it stops it from
1168 * giving a not-linked error upon destruction
1170 fakesink
= gst_element_factory_make("fakesink", NULL
);
1171 g_object_set(fakesink
,
1174 "enable-last-sample", FALSE
,
1176 gst_bin_add(GST_BIN(bin
), fakesink
);
1177 gst_element_link(tee
, fakesink
);
1180 gst_object_ref(ret
);
1181 gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(
1186 tee
= gst_bin_get_by_name(GST_BIN(ret
), "tee");
1187 pad
= gst_element_get_request_pad(tee
, "src_%u");
1188 gst_object_unref(tee
);
1189 ghost
= gst_ghost_pad_new(NULL
, pad
);
1190 gst_object_unref(pad
);
1191 g_signal_connect(GST_PAD(ghost
), "unlinked",
1192 G_CALLBACK(request_pad_unlinked_cb
), NULL
);
1193 gst_pad_set_active(ghost
, TRUE
);
1194 gst_element_add_pad(ret
, ghost
);
1196 ret
= purple_media_element_info_call_create(info
,
1197 media
, session_id
, participant
);
1198 if (element_type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1199 GstPad
*pad
= gst_element_get_static_pad(ret
, "src");
1200 g_signal_connect(pad
, "unlinked",
1201 G_CALLBACK(nonunique_src_unlinked_cb
), NULL
);
1202 gst_object_unref(pad
);
1203 gst_object_ref(ret
);
1204 gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(manager
)),
1210 purple_debug_error("media", "Error creating source or sink\n");
1215 PurpleMediaElementInfo
*
1216 purple_media_manager_get_element_info(PurpleMediaManager
*manager
,
1221 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
1222 g_return_val_if_fail(id
!= NULL
, NULL
);
1224 iter
= manager
->priv
->elements
;
1226 for (; iter
; iter
= g_list_next(iter
)) {
1228 purple_media_element_info_get_id(iter
->data
);
1229 if (purple_strequal(element_id
, id
)) {
1231 g_object_ref(iter
->data
);
1241 element_info_to_detail(PurpleMediaElementInfo
*info
)
1243 PurpleMediaElementType type
;
1245 type
= purple_media_element_info_get_element_type(info
);
1247 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1248 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1249 return g_quark_from_string("audiosrc");
1250 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1251 return g_quark_from_string("audiosink");
1253 } else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1254 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1255 return g_quark_from_string("videosrc");
1256 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1257 return g_quark_from_string("videosink");
1265 purple_media_manager_register_element(PurpleMediaManager
*manager
,
1266 PurpleMediaElementInfo
*info
)
1268 PurpleMediaElementInfo
*info2
;
1272 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1273 g_return_val_if_fail(info
!= NULL
, FALSE
);
1275 id
= purple_media_element_info_get_id(info
);
1276 info2
= purple_media_manager_get_element_info(manager
, id
);
1279 if (info2
!= NULL
) {
1280 g_object_unref(info2
);
1284 manager
->priv
->elements
=
1285 g_list_prepend(manager
->priv
->elements
, info
);
1287 detail
= element_info_to_detail(info
);
1289 g_signal_emit(manager
,
1290 purple_media_manager_signals
[ELEMENTS_CHANGED
],
1298 purple_media_manager_unregister_element(PurpleMediaManager
*manager
,
1301 PurpleMediaElementInfo
*info
;
1304 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1306 info
= purple_media_manager_get_element_info(manager
, id
);
1309 g_object_unref(info
);
1313 if (manager
->priv
->audio_src
== info
)
1314 manager
->priv
->audio_src
= NULL
;
1315 if (manager
->priv
->audio_sink
== info
)
1316 manager
->priv
->audio_sink
= NULL
;
1317 if (manager
->priv
->video_src
== info
)
1318 manager
->priv
->video_src
= NULL
;
1319 if (manager
->priv
->video_sink
== info
)
1320 manager
->priv
->video_sink
= NULL
;
1322 detail
= element_info_to_detail(info
);
1324 manager
->priv
->elements
= g_list_remove(
1325 manager
->priv
->elements
, info
);
1326 g_object_unref(info
);
1329 g_signal_emit(manager
,
1330 purple_media_manager_signals
[ELEMENTS_CHANGED
],
1338 purple_media_manager_set_active_element(PurpleMediaManager
*manager
,
1339 PurpleMediaElementInfo
*info
)
1341 PurpleMediaElementInfo
*info2
;
1342 PurpleMediaElementType type
;
1343 gboolean ret
= FALSE
;
1346 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1347 g_return_val_if_fail(info
!= NULL
, FALSE
);
1349 id
= purple_media_element_info_get_id(info
);
1350 info2
= purple_media_manager_get_element_info(manager
, id
);
1354 purple_media_manager_register_element(manager
, info
);
1356 g_object_unref(info2
);
1358 type
= purple_media_element_info_get_element_type(info
);
1360 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1361 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1362 manager
->priv
->audio_src
= info
;
1365 if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1366 manager
->priv
->video_src
= info
;
1370 if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1371 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1372 manager
->priv
->audio_sink
= info
;
1375 if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1376 manager
->priv
->video_sink
= info
;
1384 PurpleMediaElementInfo
*
1385 purple_media_manager_get_active_element(PurpleMediaManager
*manager
,
1386 PurpleMediaElementType type
)
1388 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
1390 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1391 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
)
1392 return manager
->priv
->audio_src
;
1393 else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
)
1394 return manager
->priv
->video_src
;
1395 else if (type
& PURPLE_MEDIA_ELEMENT_APPLICATION
)
1396 return get_send_application_element_info ();
1397 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1398 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
)
1399 return manager
->priv
->audio_sink
;
1400 else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
)
1401 return manager
->priv
->video_sink
;
1402 else if (type
& PURPLE_MEDIA_ELEMENT_APPLICATION
)
1403 return get_recv_application_element_info ();
1411 window_id_cb(GstBus
*bus
, GstMessage
*msg
, PurpleMediaOutputWindow
*ow
)
1415 if (GST_MESSAGE_TYPE(msg
) != GST_MESSAGE_ELEMENT
1416 || !gst_is_video_overlay_prepare_window_handle_message(msg
))
1419 sink
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
1420 while (sink
!= ow
->sink
) {
1423 sink
= GST_ELEMENT_PARENT(sink
);
1426 g_signal_handlers_disconnect_matched(bus
, G_SIGNAL_MATCH_FUNC
1427 | G_SIGNAL_MATCH_DATA
, 0, 0, NULL
,
1430 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg
)),
1436 purple_media_manager_create_output_window(PurpleMediaManager
*manager
,
1437 PurpleMedia
*media
, const gchar
*session_id
,
1438 const gchar
*participant
)
1443 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1445 iter
= manager
->priv
->output_windows
;
1446 for(; iter
; iter
= g_list_next(iter
)) {
1447 PurpleMediaOutputWindow
*ow
= iter
->data
;
1449 if (ow
->sink
== NULL
&& ow
->media
== media
&&
1450 purple_strequal(participant
, ow
->participant
) &&
1451 purple_strequal(session_id
, ow
->session_id
)) {
1453 GstElement
*queue
, *convert
, *scale
;
1454 GstElement
*tee
= purple_media_get_tee(media
,
1455 session_id
, participant
);
1460 queue
= gst_element_factory_make("queue", NULL
);
1461 convert
= gst_element_factory_make("videoconvert", NULL
);
1462 scale
= gst_element_factory_make("videoscale", NULL
);
1463 ow
->sink
= purple_media_manager_get_element(
1464 manager
, PURPLE_MEDIA_RECV_VIDEO
,
1465 ow
->media
, ow
->session_id
,
1468 if (participant
== NULL
) {
1469 /* aka this is a preview sink */
1470 GObjectClass
*klass
=
1471 G_OBJECT_GET_CLASS(ow
->sink
);
1472 if (g_object_class_find_property(klass
,
1474 g_object_set(G_OBJECT(ow
->sink
),
1475 "sync", FALSE
, NULL
);
1476 if (g_object_class_find_property(klass
,
1478 g_object_set(G_OBJECT(ow
->sink
),
1479 "async", FALSE
, NULL
);
1482 gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee
)),
1483 queue
, convert
, scale
, ow
->sink
, NULL
);
1485 bus
= gst_pipeline_get_bus(GST_PIPELINE(
1486 manager
->priv
->pipeline
));
1487 g_signal_connect(bus
, "sync-message::element",
1488 G_CALLBACK(window_id_cb
), ow
);
1489 gst_object_unref(bus
);
1491 gst_element_set_state(ow
->sink
, GST_STATE_PLAYING
);
1492 gst_element_set_state(scale
, GST_STATE_PLAYING
);
1493 gst_element_set_state(convert
, GST_STATE_PLAYING
);
1494 gst_element_set_state(queue
, GST_STATE_PLAYING
);
1495 gst_element_link(scale
, ow
->sink
);
1496 gst_element_link(convert
, scale
);
1497 gst_element_link(queue
, convert
);
1498 gst_element_link(tee
, queue
);
1508 purple_media_manager_set_output_window(PurpleMediaManager
*manager
,
1509 PurpleMedia
*media
, const gchar
*session_id
,
1510 const gchar
*participant
, gulong window_id
)
1513 PurpleMediaOutputWindow
*output_window
;
1515 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1516 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1518 output_window
= g_new0(PurpleMediaOutputWindow
, 1);
1519 output_window
->id
= manager
->priv
->next_output_window_id
++;
1520 output_window
->media
= media
;
1521 output_window
->session_id
= g_strdup(session_id
);
1522 output_window
->participant
= g_strdup(participant
);
1523 output_window
->window_id
= window_id
;
1525 manager
->priv
->output_windows
= g_list_prepend(
1526 manager
->priv
->output_windows
, output_window
);
1528 if (purple_media_get_tee(media
, session_id
, participant
) != NULL
)
1529 purple_media_manager_create_output_window(manager
,
1530 media
, session_id
, participant
);
1532 return output_window
->id
;
1539 purple_media_manager_remove_output_window(PurpleMediaManager
*manager
,
1540 gulong output_window_id
)
1543 PurpleMediaOutputWindow
*output_window
= NULL
;
1546 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1548 iter
= manager
->priv
->output_windows
;
1549 for (; iter
; iter
= g_list_next(iter
)) {
1550 PurpleMediaOutputWindow
*ow
= iter
->data
;
1551 if (ow
->id
== output_window_id
) {
1552 manager
->priv
->output_windows
= g_list_delete_link(
1553 manager
->priv
->output_windows
, iter
);
1559 if (output_window
== NULL
)
1562 if (output_window
->sink
!= NULL
) {
1563 GstElement
*element
= output_window
->sink
;
1564 GstPad
*teepad
= NULL
;
1565 GSList
*to_remove
= NULL
;
1567 /* Find the tee element this output is connected to. */
1571 GstElementFactory
*factory
;
1572 const gchar
*factory_name
;
1574 to_remove
= g_slist_append(to_remove
, element
);
1576 pad
= gst_element_get_static_pad(element
, "sink");
1577 peer
= gst_pad_get_peer(pad
);
1579 /* Output is disconnected from the pipeline. */
1580 gst_object_unref(pad
);
1584 factory
= gst_element_get_factory(GST_PAD_PARENT(peer
));
1585 factory_name
= gst_plugin_feature_get_name(factory
);
1586 if (purple_strequal(factory_name
, "tee")) {
1590 element
= GST_PAD_PARENT(peer
);
1592 gst_object_unref(pad
);
1593 gst_object_unref(peer
);
1597 gst_element_release_request_pad(GST_PAD_PARENT(teepad
),
1602 GstElement
*element
= to_remove
->data
;
1604 gst_element_set_locked_state(element
, TRUE
);
1605 gst_element_set_state(element
, GST_STATE_NULL
);
1606 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(element
)),
1608 to_remove
= g_slist_delete_link(to_remove
, to_remove
);
1612 g_free(output_window
->session_id
);
1613 g_free(output_window
->participant
);
1614 g_free(output_window
);
1623 purple_media_manager_remove_output_windows(PurpleMediaManager
*manager
,
1624 PurpleMedia
*media
, const gchar
*session_id
,
1625 const gchar
*participant
)
1630 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1632 iter
= manager
->priv
->output_windows
;
1635 PurpleMediaOutputWindow
*ow
= iter
->data
;
1636 iter
= g_list_next(iter
);
1638 if (media
== ow
->media
&&
1639 purple_strequal(session_id
, ow
->session_id
) &&
1640 purple_strequal(participant
, ow
->participant
))
1641 purple_media_manager_remove_output_window(
1648 purple_media_manager_set_ui_caps(PurpleMediaManager
*manager
,
1649 PurpleMediaCaps caps
)
1652 PurpleMediaCaps oldcaps
;
1654 g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
));
1656 oldcaps
= manager
->priv
->ui_caps
;
1657 manager
->priv
->ui_caps
= caps
;
1659 if (caps
!= oldcaps
)
1660 g_signal_emit(manager
,
1661 purple_media_manager_signals
[UI_CAPS_CHANGED
],
1667 purple_media_manager_get_ui_caps(PurpleMediaManager
*manager
)
1670 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
),
1671 PURPLE_MEDIA_CAPS_NONE
);
1672 return manager
->priv
->ui_caps
;
1674 return PURPLE_MEDIA_CAPS_NONE
;
1679 purple_media_manager_set_backend_type(PurpleMediaManager
*manager
,
1683 g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
));
1685 manager
->priv
->backend_type
= backend_type
;
1690 purple_media_manager_get_backend_type(PurpleMediaManager
*manager
)
1693 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
),
1694 PURPLE_MEDIA_CAPS_NONE
);
1696 return manager
->priv
->backend_type
;
1703 purple_media_manager_set_application_data_callbacks(PurpleMediaManager
*manager
,
1704 PurpleMedia
*media
, const gchar
*session_id
,
1705 const gchar
*participant
, PurpleMediaAppDataCallbacks
*callbacks
,
1706 gpointer user_data
, GDestroyNotify notify
)
1708 #ifdef HAVE_MEDIA_APPLICATION
1709 PurpleMediaAppDataInfo
* info
= ensure_app_data_info_and_lock (manager
,
1710 media
, session_id
, participant
);
1713 info
->notify (info
->user_data
);
1715 if (info
->readable_cb_token
) {
1716 g_source_remove (info
->readable_timer_id
);
1717 info
->readable_cb_token
= 0;
1720 if (info
->writable_cb_token
) {
1721 g_source_remove (info
->writable_timer_id
);
1722 info
->writable_cb_token
= 0;
1726 info
->callbacks
= *callbacks
;
1728 info
->callbacks
.writable
= NULL
;
1729 info
->callbacks
.readable
= NULL
;
1731 info
->user_data
= user_data
;
1732 info
->notify
= notify
;
1734 call_appsrc_writable_locked (info
);
1735 if (info
->num_samples
> 0 || info
->current_sample
!= NULL
)
1736 call_appsink_readable_locked (info
);
1738 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1743 purple_media_manager_send_application_data (
1744 PurpleMediaManager
*manager
, PurpleMedia
*media
, const gchar
*session_id
,
1745 const gchar
*participant
, gpointer buffer
, guint size
, gboolean blocking
)
1747 #ifdef HAVE_MEDIA_APPLICATION
1748 PurpleMediaAppDataInfo
* info
= get_app_data_info_and_lock (manager
,
1749 media
, session_id
, participant
);
1751 if (info
&& info
->appsrc
&& info
->connected
) {
1752 GstBuffer
*gstbuffer
= gst_buffer_new_wrapped (g_memdup (buffer
, size
),
1754 GstAppSrc
*appsrc
= gst_object_ref (info
->appsrc
);
1756 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1757 if (gst_app_src_push_buffer (appsrc
, gstbuffer
) == GST_FLOW_OK
) {
1761 srcpad
= gst_element_get_static_pad (GST_ELEMENT (appsrc
),
1764 gst_pad_peer_query (srcpad
, gst_query_new_drain ());
1765 gst_object_unref (srcpad
);
1768 gst_object_unref (appsrc
);
1771 gst_object_unref (appsrc
);
1775 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1783 purple_media_manager_receive_application_data (
1784 PurpleMediaManager
*manager
, PurpleMedia
*media
, const gchar
*session_id
,
1785 const gchar
*participant
, gpointer buffer
, guint max_size
,
1788 #ifdef HAVE_MEDIA_APPLICATION
1789 PurpleMediaAppDataInfo
* info
= get_app_data_info_and_lock (manager
,
1790 media
, session_id
, participant
);
1791 guint bytes_read
= 0;
1794 /* If we are in a blocking read, we need to loop until max_size data
1795 * is read into the buffer, if we're not, then we need to read as much
1799 if (!info
->current_sample
&& info
->appsink
&& info
->num_samples
> 0) {
1800 info
->current_sample
= gst_app_sink_pull_sample (info
->appsink
);
1801 info
->sample_offset
= 0;
1802 if (info
->current_sample
)
1803 info
->num_samples
--;
1806 if (info
->current_sample
) {
1807 GstBuffer
*gstbuffer
= gst_sample_get_buffer (
1808 info
->current_sample
);
1812 guint bytes_to_copy
;
1814 gst_buffer_map (gstbuffer
, &mapinfo
, GST_MAP_READ
);
1815 /* We must copy only the data remaining in the buffer without
1816 * overflowing the buffer */
1817 bytes_to_copy
= max_size
- bytes_read
;
1818 if (bytes_to_copy
> mapinfo
.size
- info
->sample_offset
)
1819 bytes_to_copy
= mapinfo
.size
- info
->sample_offset
;
1820 memcpy ((guint8
*)buffer
+ bytes_read
,
1821 mapinfo
.data
+ info
->sample_offset
, bytes_to_copy
);
1823 gst_buffer_unmap (gstbuffer
, &mapinfo
);
1824 info
->sample_offset
+= bytes_to_copy
;
1825 bytes_read
+= bytes_to_copy
;
1826 if (info
->sample_offset
== mapinfo
.size
) {
1827 gst_sample_unref (info
->current_sample
);
1828 info
->current_sample
= NULL
;
1829 info
->sample_offset
= 0;
1832 /* In case there's no buffer in the sample (should never
1833 * happen), we need to at least unref it */
1834 gst_sample_unref (info
->current_sample
);
1835 info
->current_sample
= NULL
;
1836 info
->sample_offset
= 0;
1840 /* If blocking, wait until there's an available sample */
1841 while (bytes_read
< max_size
&& blocking
&&
1842 info
->current_sample
== NULL
&& info
->num_samples
== 0) {
1843 g_cond_wait (&info
->readable_cond
, &manager
->priv
->appdata_mutex
);
1845 /* We've been signaled, we need to unlock and regrab the info
1846 * struct to make sure nothing changed */
1847 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1848 info
= get_app_data_info_and_lock (manager
,
1849 media
, session_id
, participant
);
1850 if (info
== NULL
|| info
->appsink
== NULL
) {
1851 /* The session was destroyed while we were waiting, we
1852 * should return here */
1853 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1857 } while (bytes_read
< max_size
&&
1858 (blocking
|| info
->num_samples
> 0));
1860 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1863 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1873 videosink_disable_last_sample(GstElement
*sink
)
1875 GObjectClass
*klass
= G_OBJECT_GET_CLASS(sink
);
1877 if (g_object_class_find_property(klass
, "enable-last-sample")) {
1878 g_object_set(sink
, "enable-last-sample", FALSE
, NULL
);
1882 #if GST_CHECK_VERSION(1, 4, 0)
1884 static PurpleMediaElementType
1885 gst_class_to_purple_element_type(const gchar
*device_class
)
1887 if (purple_strequal(device_class
, "Audio/Source")) {
1888 return PURPLE_MEDIA_ELEMENT_AUDIO
1889 | PURPLE_MEDIA_ELEMENT_SRC
1890 | PURPLE_MEDIA_ELEMENT_ONE_SRC
1891 | PURPLE_MEDIA_ELEMENT_UNIQUE
;
1892 } else if (purple_strequal(device_class
, "Audio/Sink")) {
1893 return PURPLE_MEDIA_ELEMENT_AUDIO
1894 | PURPLE_MEDIA_ELEMENT_SINK
1895 | PURPLE_MEDIA_ELEMENT_ONE_SINK
;
1896 } else if (purple_strequal(device_class
, "Video/Source")) {
1897 return PURPLE_MEDIA_ELEMENT_VIDEO
1898 | PURPLE_MEDIA_ELEMENT_SRC
1899 | PURPLE_MEDIA_ELEMENT_ONE_SRC
1900 | PURPLE_MEDIA_ELEMENT_UNIQUE
;
1901 } else if (purple_strequal(device_class
, "Video/Sink")) {
1902 return PURPLE_MEDIA_ELEMENT_VIDEO
1903 | PURPLE_MEDIA_ELEMENT_SINK
1904 | PURPLE_MEDIA_ELEMENT_ONE_SINK
;
1907 return PURPLE_MEDIA_ELEMENT_NONE
;
1911 gst_device_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
1912 const gchar
*session_id
, const gchar
*participant
)
1916 PurpleMediaElementType type
;
1918 device
= g_object_get_data(G_OBJECT(info
), "gst-device");
1923 result
= gst_device_create_element(device
, NULL
);
1928 type
= purple_media_element_info_get_element_type(info
);
1930 if ((type
& PURPLE_MEDIA_ELEMENT_VIDEO
) &&
1931 (type
& PURPLE_MEDIA_ELEMENT_SINK
)) {
1932 videosink_disable_last_sample(result
);
1939 device_is_ignored(GstDevice
*device
)
1941 gboolean result
= FALSE
;
1943 #if GST_CHECK_VERSION(1, 6, 0)
1944 gchar
*device_class
;
1946 g_return_val_if_fail(device
, TRUE
);
1948 device_class
= gst_device_get_device_class(device
);
1950 /* Ignore PulseAudio monitor audio sources since they have little use
1951 * in the context of telephony.*/
1952 if (purple_strequal(device_class
, "Audio/Source")) {
1953 GstStructure
*properties
;
1954 const gchar
*pa_class
;
1956 properties
= gst_device_get_properties(device
);
1958 pa_class
= gst_structure_get_string(properties
, "device.class");
1959 if (purple_strequal(pa_class
, "monitor")) {
1963 gst_structure_free(properties
);
1966 g_free(device_class
);
1967 #endif /* GST_CHECK_VERSION(1, 6, 0) */
1973 purple_media_manager_register_gst_device(PurpleMediaManager
*manager
,
1976 PurpleMediaElementInfo
*info
;
1977 PurpleMediaElementType type
;
1979 gchar
*device_class
;
1982 if (device_is_ignored(device
)) {
1986 name
= gst_device_get_display_name(device
);
1987 device_class
= gst_device_get_device_class(device
);
1989 id
= g_strdup_printf("%s %s", device_class
, name
);
1991 type
= gst_class_to_purple_element_type(device_class
);
1993 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1997 "create-cb", gst_device_create_cb
,
2000 g_object_set_data(G_OBJECT(info
), "gst-device", device
);
2002 purple_media_manager_register_element(manager
, info
);
2004 purple_debug_info("mediamanager", "Registered %s device %s",
2005 device_class
, name
);
2008 g_free(device_class
);
2013 purple_media_manager_unregister_gst_device(PurpleMediaManager
*manager
,
2018 gchar
*device_class
;
2019 gboolean done
= FALSE
;
2021 name
= gst_device_get_display_name(device
);
2022 device_class
= gst_device_get_device_class(device
);
2024 for (i
= manager
->priv
->elements
; i
&& !done
; i
= i
->next
) {
2025 PurpleMediaElementInfo
*info
= i
->data
;
2028 device2
= g_object_get_data(G_OBJECT(info
), "gst-device");
2031 gchar
*device_class2
;
2033 name2
= gst_device_get_display_name(device2
);
2034 device_class2
= gst_device_get_device_class(device2
);
2036 if (purple_strequal(name
, name2
) &&
2037 purple_strequal(device_class
, device_class2
)) {
2040 id
= purple_media_element_info_get_id(info
);
2041 purple_media_manager_unregister_element(manager
,
2044 purple_debug_info("mediamanager",
2045 "Unregistered %s device %s",
2046 device_class
, name
);
2054 g_free(device_class2
);
2059 g_free(device_class
);
2063 device_monitor_bus_cb(GstBus
*bus
, GstMessage
*message
, gpointer user_data
)
2065 PurpleMediaManager
*manager
= user_data
;
2066 GstMessageType message_type
;
2069 message_type
= GST_MESSAGE_TYPE(message
);
2071 if (message_type
== GST_MESSAGE_DEVICE_ADDED
) {
2072 gst_message_parse_device_added(message
, &device
);
2073 purple_media_manager_register_gst_device(manager
, device
);
2074 } else if (message_type
== GST_MESSAGE_DEVICE_REMOVED
) {
2075 gst_message_parse_device_removed (message
, &device
);
2076 purple_media_manager_unregister_gst_device(manager
, device
);
2079 return G_SOURCE_CONTINUE
;
2082 #endif /* GST_CHECK_VERSION(1, 4, 0) */
2085 purple_media_manager_init_device_monitor(PurpleMediaManager
*manager
)
2087 #if GST_CHECK_VERSION(1, 4, 0)
2091 manager
->priv
->device_monitor
= gst_device_monitor_new();
2093 bus
= gst_device_monitor_get_bus(manager
->priv
->device_monitor
);
2094 gst_bus_add_watch (bus
, device_monitor_bus_cb
, manager
);
2095 gst_object_unref (bus
);
2097 /* This avoids warning in GStreamer logs about no filters set */
2098 gst_device_monitor_add_filter(manager
->priv
->device_monitor
, NULL
, NULL
);
2100 gst_device_monitor_start(manager
->priv
->device_monitor
);
2102 i
= gst_device_monitor_get_devices(manager
->priv
->device_monitor
);
2103 for (; i
; i
= g_list_delete_link(i
, i
)) {
2104 GstDevice
*device
= i
->data
;
2106 purple_media_manager_register_gst_device(manager
, device
);
2107 gst_object_unref(device
);
2109 #endif /* GST_CHECK_VERSION(1, 4, 0) */
2113 purple_media_manager_enumerate_elements(PurpleMediaManager
*manager
,
2114 PurpleMediaElementType type
)
2116 GList
*result
= NULL
;
2119 for (i
= manager
->priv
->elements
; i
; i
= i
->next
) {
2120 PurpleMediaElementInfo
*info
= i
->data
;
2121 PurpleMediaElementType type2
;
2123 type2
= purple_media_element_info_get_element_type(info
);
2125 if ((type2
& type
) == type
) {
2127 result
= g_list_prepend(result
, info
);
2135 gst_factory_make_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2136 const gchar
*session_id
, const gchar
*participant
)
2139 GstElement
*element
;
2141 id
= purple_media_element_info_get_id(info
);
2143 element
= gst_element_factory_make(id
, NULL
);
2151 autovideosink_child_added_cb (GstChildProxy
*child_proxy
, GObject
*object
,
2152 gchar
*name
, gpointer user_data
)
2154 videosink_disable_last_sample(GST_ELEMENT(object
));
2158 default_video_sink_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2159 const gchar
*session_id
, const gchar
*participant
)
2161 GstElement
*videosink
= gst_element_factory_make("autovideosink", NULL
);
2163 g_signal_connect(videosink
, "child-added",
2164 G_CALLBACK(autovideosink_child_added_cb
), NULL
);
2170 disabled_video_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2171 const gchar
*session_id
, const gchar
*participant
)
2173 GstElement
*src
= gst_element_factory_make("videotestsrc", NULL
);
2175 /* GST_VIDEO_TEST_SRC_BLACK */
2176 g_object_set(src
, "pattern", 2, NULL
);
2182 test_video_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2183 const gchar
*session_id
, const gchar
*participant
)
2185 GstElement
*src
= gst_element_factory_make("videotestsrc", NULL
);
2187 g_object_set(src
, "is-live", TRUE
, NULL
);
2193 purple_media_manager_register_static_elements(PurpleMediaManager
*manager
)
2195 static const gchar
*VIDEO_SINK_PLUGINS
[] = {
2196 /* "aasink", "AALib", Didn't work for me */
2197 "directdrawsink", "DirectDraw",
2198 "glimagesink", "OpenGL",
2199 "ximagesink", "X Window System",
2200 "xvimagesink", "X Window System (Xv)",
2203 const gchar
**sinks
= VIDEO_SINK_PLUGINS
;
2205 /* Default auto* elements. */
2207 purple_media_manager_register_element(manager
,
2208 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2209 "id", "autoaudiosrc",
2210 "name", N_("Default"),
2211 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2212 | PURPLE_MEDIA_ELEMENT_SRC
2213 | PURPLE_MEDIA_ELEMENT_ONE_SRC
2214 | PURPLE_MEDIA_ELEMENT_UNIQUE
,
2215 "create-cb", gst_factory_make_cb
,
2218 purple_media_manager_register_element(manager
,
2219 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2220 "id", "autoaudiosink",
2221 "name", N_("Default"),
2222 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2223 | PURPLE_MEDIA_ELEMENT_SINK
2224 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2225 "create-cb", gst_factory_make_cb
,
2228 purple_media_manager_register_element(manager
,
2229 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2230 "id", "autovideosrc",
2231 "name", N_("Default"),
2232 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2233 | PURPLE_MEDIA_ELEMENT_SRC
2234 | PURPLE_MEDIA_ELEMENT_ONE_SRC
2235 | PURPLE_MEDIA_ELEMENT_UNIQUE
,
2236 "create-cb", gst_factory_make_cb
,
2239 purple_media_manager_register_element(manager
,
2240 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2241 "id", "autovideosink",
2242 "name", N_("Default"),
2243 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2244 | PURPLE_MEDIA_ELEMENT_SINK
2245 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2246 "create-cb", default_video_sink_create_cb
,
2249 /* Special elements */
2251 purple_media_manager_register_element(manager
,
2252 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2253 "id", "audiotestsrc",
2254 /* Translators: This is a noun that refers to one
2255 * possible audio input device. The device can help the
2256 * user to check if her speakers or headphones have been
2257 * set up correctly for voice calling. */
2258 "name", N_("Test Sound"),
2259 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2260 | PURPLE_MEDIA_ELEMENT_SRC
2261 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2262 "create-cb", gst_factory_make_cb
,
2265 purple_media_manager_register_element(manager
,
2266 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2267 "id", "disabledvideosrc",
2268 "name", N_("Disabled"),
2269 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2270 | PURPLE_MEDIA_ELEMENT_SRC
2271 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2272 "create-cb", disabled_video_create_cb
,
2275 purple_media_manager_register_element(manager
,
2276 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2277 "id", "videotestsrc",
2278 /* Translators: This is a noun that refers to one
2279 * possible video input device. The device produces
2280 * a test "monoscope" image that can help the user check
2281 * the video output has been set up correctly without
2282 * needing a webcam connected to the computer. */
2283 "name", N_("Test Pattern"),
2284 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2285 | PURPLE_MEDIA_ELEMENT_SRC
2286 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2287 "create-cb", test_video_create_cb
,
2290 for (sinks
= VIDEO_SINK_PLUGINS
; sinks
[0]; sinks
+= 2) {
2291 GstElementFactory
*factory
;
2293 factory
= gst_element_factory_find(sinks
[0]);
2298 purple_media_manager_register_element(manager
,
2299 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2302 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2303 | PURPLE_MEDIA_ELEMENT_SINK
2304 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2305 "create-cb", gst_factory_make_cb
,
2308 gst_object_unref(factory
);
2313 * PurpleMediaElementType
2317 purple_media_element_type_get_type()
2319 static GType type
= 0;
2321 static const GFlagsValue values
[] = {
2322 { PURPLE_MEDIA_ELEMENT_NONE
,
2323 "PURPLE_MEDIA_ELEMENT_NONE", "none" },
2324 { PURPLE_MEDIA_ELEMENT_AUDIO
,
2325 "PURPLE_MEDIA_ELEMENT_AUDIO", "audio" },
2326 { PURPLE_MEDIA_ELEMENT_VIDEO
,
2327 "PURPLE_MEDIA_ELEMENT_VIDEO", "video" },
2328 { PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO
,
2329 "PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO",
2331 { PURPLE_MEDIA_ELEMENT_NO_SRCS
,
2332 "PURPLE_MEDIA_ELEMENT_NO_SRCS", "no-srcs" },
2333 { PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2334 "PURPLE_MEDIA_ELEMENT_ONE_SRC", "one-src" },
2335 { PURPLE_MEDIA_ELEMENT_MULTI_SRC
,
2336 "PURPLE_MEDIA_ELEMENT_MULTI_SRC",
2338 { PURPLE_MEDIA_ELEMENT_REQUEST_SRC
,
2339 "PURPLE_MEDIA_ELEMENT_REQUEST_SRC",
2341 { PURPLE_MEDIA_ELEMENT_NO_SINKS
,
2342 "PURPLE_MEDIA_ELEMENT_NO_SINKS", "no-sinks" },
2343 { PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2344 "PURPLE_MEDIA_ELEMENT_ONE_SINK", "one-sink" },
2345 { PURPLE_MEDIA_ELEMENT_MULTI_SINK
,
2346 "PURPLE_MEDIA_ELEMENT_MULTI_SINK",
2348 { PURPLE_MEDIA_ELEMENT_REQUEST_SINK
,
2349 "PURPLE_MEDIA_ELEMENT_REQUEST_SINK",
2351 { PURPLE_MEDIA_ELEMENT_UNIQUE
,
2352 "PURPLE_MEDIA_ELEMENT_UNIQUE", "unique" },
2353 { PURPLE_MEDIA_ELEMENT_SRC
,
2354 "PURPLE_MEDIA_ELEMENT_SRC", "src" },
2355 { PURPLE_MEDIA_ELEMENT_SINK
,
2356 "PURPLE_MEDIA_ELEMENT_SINK", "sink" },
2357 { PURPLE_MEDIA_ELEMENT_APPLICATION
,
2358 "PURPLE_MEDIA_ELEMENT_APPLICATION", "application" },
2361 type
= g_flags_register_static(
2362 "PurpleMediaElementType", values
);
2369 * PurpleMediaElementInfo
2372 struct _PurpleMediaElementInfoClass
2374 GObjectClass parent_class
;
2377 struct _PurpleMediaElementInfo
2383 struct _PurpleMediaElementInfoPrivate
2387 PurpleMediaElementType type
;
2388 PurpleMediaElementCreateCallback create
;
2399 G_DEFINE_TYPE_WITH_PRIVATE(PurpleMediaElementInfo
,
2400 purple_media_element_info
, G_TYPE_OBJECT
);
2403 purple_media_element_info_init(PurpleMediaElementInfo
*info
)
2405 PurpleMediaElementInfoPrivate
*priv
=
2406 purple_media_element_info_get_instance_private(info
);
2409 priv
->type
= PURPLE_MEDIA_ELEMENT_NONE
;
2410 priv
->create
= NULL
;
2414 purple_media_element_info_finalize(GObject
*info
)
2416 PurpleMediaElementInfoPrivate
*priv
=
2417 purple_media_element_info_get_instance_private(
2418 PURPLE_MEDIA_ELEMENT_INFO(info
));
2422 G_OBJECT_CLASS(purple_media_element_info_parent_class
)->finalize(info
);
2426 purple_media_element_info_set_property (GObject
*object
, guint prop_id
,
2427 const GValue
*value
, GParamSpec
*pspec
)
2429 PurpleMediaElementInfoPrivate
*priv
;
2430 g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object
));
2432 priv
= purple_media_element_info_get_instance_private(
2433 PURPLE_MEDIA_ELEMENT_INFO(object
));
2438 priv
->id
= g_value_dup_string(value
);
2442 priv
->name
= g_value_dup_string(value
);
2445 priv
->type
= g_value_get_flags(value
);
2448 case PROP_CREATE_CB
:
2449 priv
->create
= g_value_get_pointer(value
);
2452 G_OBJECT_WARN_INVALID_PROPERTY_ID(
2453 object
, prop_id
, pspec
);
2459 purple_media_element_info_get_property (GObject
*object
, guint prop_id
,
2460 GValue
*value
, GParamSpec
*pspec
)
2462 PurpleMediaElementInfoPrivate
*priv
;
2463 g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object
));
2465 priv
= purple_media_element_info_get_instance_private(
2466 PURPLE_MEDIA_ELEMENT_INFO(object
));
2470 g_value_set_string(value
, priv
->id
);
2473 g_value_set_string(value
, priv
->name
);
2476 g_value_set_flags(value
, priv
->type
);
2478 case PROP_CREATE_CB
:
2479 g_value_set_pointer(value
, priv
->create
);
2482 G_OBJECT_WARN_INVALID_PROPERTY_ID(
2483 object
, prop_id
, pspec
);
2489 purple_media_element_info_class_init(PurpleMediaElementInfoClass
*klass
)
2491 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
2493 gobject_class
->finalize
= purple_media_element_info_finalize
;
2494 gobject_class
->set_property
= purple_media_element_info_set_property
;
2495 gobject_class
->get_property
= purple_media_element_info_get_property
;
2497 g_object_class_install_property(gobject_class
, PROP_ID
,
2498 g_param_spec_string("id",
2500 "The unique identifier of the element.",
2502 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2503 G_PARAM_STATIC_STRINGS
));
2505 g_object_class_install_property(gobject_class
, PROP_NAME
,
2506 g_param_spec_string("name",
2508 "The friendly/display name of this element.",
2510 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2511 G_PARAM_STATIC_STRINGS
));
2513 g_object_class_install_property(gobject_class
, PROP_TYPE
,
2514 g_param_spec_flags("type",
2516 "The type of element this is.",
2517 PURPLE_TYPE_MEDIA_ELEMENT_TYPE
,
2518 PURPLE_MEDIA_ELEMENT_NONE
,
2519 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2520 G_PARAM_STATIC_STRINGS
));
2522 g_object_class_install_property(gobject_class
, PROP_CREATE_CB
,
2523 g_param_spec_pointer("create-cb",
2525 "The function called to create this element.",
2526 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2527 G_PARAM_STATIC_STRINGS
));
2531 purple_media_element_info_get_id(PurpleMediaElementInfo
*info
)
2535 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2536 g_object_get(info
, "id", &id
, NULL
);
2541 purple_media_element_info_get_name(PurpleMediaElementInfo
*info
)
2544 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2545 g_object_get(info
, "name", &name
, NULL
);
2549 PurpleMediaElementType
2550 purple_media_element_info_get_element_type(PurpleMediaElementInfo
*info
)
2552 PurpleMediaElementType type
;
2553 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
),
2554 PURPLE_MEDIA_ELEMENT_NONE
);
2555 g_object_get(info
, "type", &type
, NULL
);
2560 purple_media_element_info_call_create(PurpleMediaElementInfo
*info
,
2561 PurpleMedia
*media
, const gchar
*session_id
,
2562 const gchar
*participant
)
2564 PurpleMediaElementCreateCallback create
;
2565 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2566 g_object_get(info
, "create-cb", &create
, NULL
);
2568 return create(info
, media
, session_id
, participant
);