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
);
960 readable_cb(manager
, media
, session_id
, participant
, cb_data
);
962 g_mutex_lock (&manager
->priv
->appdata_mutex
);
963 g_object_unref (media
);
965 g_free (participant
);
966 if (cb_token
!= *cb_token_ptr
) {
967 /* We got cancelled */
968 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
973 /* Do we still have samples? Schedule appsink_readable again. We break here
974 * so that other events get a chance to be processed too. */
975 if (info
->num_samples
> 0 || info
->current_sample
!= NULL
) {
978 info
->readable_cb_token
= 0;
981 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
986 call_appsink_readable_locked (PurpleMediaAppDataInfo
*info
)
988 PurpleMediaManager
*manager
= purple_media_manager_get ();
990 /* We must signal that a new sample has arrived to release blocking reads */
991 g_cond_broadcast (&info
->readable_cond
);
993 /* We already have a writable callback scheduled, don't create another one */
994 if (info
->readable_cb_token
|| info
->callbacks
.readable
== NULL
)
997 info
->readable_cb_token
= ++manager
->priv
->appdata_cb_token
;
998 info
->readable_timer_id
= g_timeout_add (0, appsink_readable
, info
);
1001 static GstFlowReturn
1002 appsink_new_sample (GstAppSink
*appsink
, gpointer user_data
)
1004 PurpleMediaManager
*manager
= purple_media_manager_get ();
1005 PurpleMediaAppDataInfo
*info
= user_data
;
1007 g_mutex_lock (&manager
->priv
->appdata_mutex
);
1008 info
->num_samples
++;
1009 call_appsink_readable_locked (info
);
1010 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1016 appsink_destroyed (PurpleMediaAppDataInfo
*info
)
1018 PurpleMediaManager
*manager
;
1021 /* PurpleMediaAppDataInfo is being freed. Return at once. */
1025 manager
= purple_media_manager_get ();
1027 g_mutex_lock (&manager
->priv
->appdata_mutex
);
1028 info
->appsink
= NULL
;
1029 info
->num_samples
= 0;
1030 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1034 create_recv_appsink(PurpleMediaElementInfo
*element_info
, PurpleMedia
*media
,
1035 const gchar
*session_id
, const gchar
*participant
)
1037 PurpleMediaManager
*manager
= purple_media_manager_get ();
1038 PurpleMediaAppDataInfo
* info
= ensure_app_data_info_and_lock (manager
,
1039 media
, session_id
, participant
);
1040 GstElement
*appsink
= (GstElement
*)info
->appsink
;
1042 if (appsink
== NULL
) {
1043 GstAppSinkCallbacks callbacks
= {appsink_eos
, appsink_new_preroll
,
1044 appsink_new_sample
, {NULL
}};
1045 GstCaps
*caps
= gst_caps_new_empty_simple ("application/octet-stream");
1047 appsink
= gst_element_factory_make("appsink", NULL
);
1049 info
->appsink
= (GstAppSink
*)appsink
;
1051 gst_app_sink_set_caps (info
->appsink
, caps
);
1052 gst_app_sink_set_callbacks (info
->appsink
,
1053 &callbacks
, info
, (GDestroyNotify
) appsink_destroyed
);
1054 gst_caps_unref (caps
);
1058 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1061 #endif /* HAVE_MEDIA_APPLICATION */
1064 static PurpleMediaElementInfo
*
1065 get_send_application_element_info ()
1067 static PurpleMediaElementInfo
*info
= NULL
;
1069 #ifdef HAVE_MEDIA_APPLICATION
1071 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1072 "id", "pidginappsrc",
1073 "name", "Pidgin Application Source",
1074 "type", PURPLE_MEDIA_ELEMENT_APPLICATION
1075 | PURPLE_MEDIA_ELEMENT_SRC
1076 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
1077 "create-cb", create_send_appsrc
, NULL
);
1084 static PurpleMediaElementInfo
*
1085 get_recv_application_element_info ()
1087 static PurpleMediaElementInfo
*info
= NULL
;
1089 #ifdef HAVE_MEDIA_APPLICATION
1091 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1092 "id", "pidginappsink",
1093 "name", "Pidgin Application Sink",
1094 "type", PURPLE_MEDIA_ELEMENT_APPLICATION
1095 | PURPLE_MEDIA_ELEMENT_SINK
1096 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
1097 "create-cb", create_recv_appsink
, NULL
);
1105 purple_media_manager_get_element(PurpleMediaManager
*manager
,
1106 PurpleMediaSessionType type
, PurpleMedia
*media
,
1107 const gchar
*session_id
, const gchar
*participant
)
1109 GstElement
*ret
= NULL
;
1110 PurpleMediaElementInfo
*info
= NULL
;
1111 PurpleMediaElementType element_type
;
1113 if (type
& PURPLE_MEDIA_SEND_AUDIO
)
1114 info
= manager
->priv
->audio_src
;
1115 else if (type
& PURPLE_MEDIA_RECV_AUDIO
)
1116 info
= manager
->priv
->audio_sink
;
1117 else if (type
& PURPLE_MEDIA_SEND_VIDEO
)
1118 info
= manager
->priv
->video_src
;
1119 else if (type
& PURPLE_MEDIA_RECV_VIDEO
)
1120 info
= manager
->priv
->video_sink
;
1121 else if (type
& PURPLE_MEDIA_SEND_APPLICATION
)
1122 info
= get_send_application_element_info ();
1123 else if (type
& PURPLE_MEDIA_RECV_APPLICATION
)
1124 info
= get_recv_application_element_info ();
1129 element_type
= purple_media_element_info_get_element_type(info
);
1131 if (element_type
& PURPLE_MEDIA_ELEMENT_UNIQUE
&&
1132 element_type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1136 gchar
*id
= purple_media_element_info_get_id(info
);
1138 ret
= gst_bin_get_by_name(GST_BIN(
1139 purple_media_manager_get_pipeline(
1143 GstElement
*bin
, *fakesink
;
1144 ret
= purple_media_element_info_call_create(info
,
1145 media
, session_id
, participant
);
1146 bin
= gst_bin_new(id
);
1147 tee
= gst_element_factory_make("tee", "tee");
1148 gst_bin_add_many(GST_BIN(bin
), ret
, tee
, NULL
);
1150 if (type
& PURPLE_MEDIA_SEND_VIDEO
) {
1151 GstElement
*videoscale
;
1152 GstElement
*capsfilter
;
1154 videoscale
= gst_element_factory_make("videoscale", NULL
);
1155 capsfilter
= gst_element_factory_make("capsfilter", "protocol_video_caps");
1157 g_object_set(G_OBJECT(capsfilter
),
1158 "caps", purple_media_manager_get_video_caps(manager
), NULL
);
1160 gst_bin_add_many(GST_BIN(bin
), videoscale
, capsfilter
, NULL
);
1161 gst_element_link_many(ret
, videoscale
, capsfilter
, tee
, NULL
);
1163 gst_element_link(ret
, tee
);
1166 * This shouldn't be necessary, but it stops it from
1167 * giving a not-linked error upon destruction
1169 fakesink
= gst_element_factory_make("fakesink", NULL
);
1170 g_object_set(fakesink
,
1173 "enable-last-sample", FALSE
,
1175 gst_bin_add(GST_BIN(bin
), fakesink
);
1176 gst_element_link(tee
, fakesink
);
1179 gst_object_ref(ret
);
1180 gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(
1185 tee
= gst_bin_get_by_name(GST_BIN(ret
), "tee");
1186 pad
= gst_element_get_request_pad(tee
, "src_%u");
1187 gst_object_unref(tee
);
1188 ghost
= gst_ghost_pad_new(NULL
, pad
);
1189 gst_object_unref(pad
);
1190 g_signal_connect(GST_PAD(ghost
), "unlinked",
1191 G_CALLBACK(request_pad_unlinked_cb
), NULL
);
1192 gst_pad_set_active(ghost
, TRUE
);
1193 gst_element_add_pad(ret
, ghost
);
1195 ret
= purple_media_element_info_call_create(info
,
1196 media
, session_id
, participant
);
1197 if (element_type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1198 GstPad
*pad
= gst_element_get_static_pad(ret
, "src");
1199 g_signal_connect(pad
, "unlinked",
1200 G_CALLBACK(nonunique_src_unlinked_cb
), NULL
);
1201 gst_object_unref(pad
);
1202 gst_object_ref(ret
);
1203 gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(manager
)),
1209 purple_debug_error("media", "Error creating source or sink\n");
1214 PurpleMediaElementInfo
*
1215 purple_media_manager_get_element_info(PurpleMediaManager
*manager
,
1220 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
1221 g_return_val_if_fail(id
!= NULL
, NULL
);
1223 iter
= manager
->priv
->elements
;
1225 for (; iter
; iter
= g_list_next(iter
)) {
1227 purple_media_element_info_get_id(iter
->data
);
1228 if (purple_strequal(element_id
, id
)) {
1230 g_object_ref(iter
->data
);
1240 element_info_to_detail(PurpleMediaElementInfo
*info
)
1242 PurpleMediaElementType type
;
1244 type
= purple_media_element_info_get_element_type(info
);
1246 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1247 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1248 return g_quark_from_string("audiosrc");
1249 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1250 return g_quark_from_string("audiosink");
1252 } else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1253 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1254 return g_quark_from_string("videosrc");
1255 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1256 return g_quark_from_string("videosink");
1264 purple_media_manager_register_element(PurpleMediaManager
*manager
,
1265 PurpleMediaElementInfo
*info
)
1267 PurpleMediaElementInfo
*info2
;
1271 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1272 g_return_val_if_fail(info
!= NULL
, FALSE
);
1274 id
= purple_media_element_info_get_id(info
);
1275 info2
= purple_media_manager_get_element_info(manager
, id
);
1278 if (info2
!= NULL
) {
1279 g_object_unref(info2
);
1283 manager
->priv
->elements
=
1284 g_list_prepend(manager
->priv
->elements
, info
);
1286 detail
= element_info_to_detail(info
);
1288 g_signal_emit(manager
,
1289 purple_media_manager_signals
[ELEMENTS_CHANGED
],
1297 purple_media_manager_unregister_element(PurpleMediaManager
*manager
,
1300 PurpleMediaElementInfo
*info
;
1303 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1305 info
= purple_media_manager_get_element_info(manager
, id
);
1308 g_object_unref(info
);
1312 if (manager
->priv
->audio_src
== info
)
1313 manager
->priv
->audio_src
= NULL
;
1314 if (manager
->priv
->audio_sink
== info
)
1315 manager
->priv
->audio_sink
= NULL
;
1316 if (manager
->priv
->video_src
== info
)
1317 manager
->priv
->video_src
= NULL
;
1318 if (manager
->priv
->video_sink
== info
)
1319 manager
->priv
->video_sink
= NULL
;
1321 detail
= element_info_to_detail(info
);
1323 manager
->priv
->elements
= g_list_remove(
1324 manager
->priv
->elements
, info
);
1325 g_object_unref(info
);
1328 g_signal_emit(manager
,
1329 purple_media_manager_signals
[ELEMENTS_CHANGED
],
1337 purple_media_manager_set_active_element(PurpleMediaManager
*manager
,
1338 PurpleMediaElementInfo
*info
)
1340 PurpleMediaElementInfo
*info2
;
1341 PurpleMediaElementType type
;
1342 gboolean ret
= FALSE
;
1345 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1346 g_return_val_if_fail(info
!= NULL
, FALSE
);
1348 id
= purple_media_element_info_get_id(info
);
1349 info2
= purple_media_manager_get_element_info(manager
, id
);
1353 purple_media_manager_register_element(manager
, info
);
1355 g_object_unref(info2
);
1357 type
= purple_media_element_info_get_element_type(info
);
1359 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1360 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1361 manager
->priv
->audio_src
= info
;
1364 if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1365 manager
->priv
->video_src
= info
;
1369 if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1370 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1371 manager
->priv
->audio_sink
= info
;
1374 if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1375 manager
->priv
->video_sink
= info
;
1383 PurpleMediaElementInfo
*
1384 purple_media_manager_get_active_element(PurpleMediaManager
*manager
,
1385 PurpleMediaElementType type
)
1387 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
1389 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1390 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
)
1391 return manager
->priv
->audio_src
;
1392 else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
)
1393 return manager
->priv
->video_src
;
1394 else if (type
& PURPLE_MEDIA_ELEMENT_APPLICATION
)
1395 return get_send_application_element_info ();
1396 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1397 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
)
1398 return manager
->priv
->audio_sink
;
1399 else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
)
1400 return manager
->priv
->video_sink
;
1401 else if (type
& PURPLE_MEDIA_ELEMENT_APPLICATION
)
1402 return get_recv_application_element_info ();
1410 window_id_cb(GstBus
*bus
, GstMessage
*msg
, PurpleMediaOutputWindow
*ow
)
1414 if (GST_MESSAGE_TYPE(msg
) != GST_MESSAGE_ELEMENT
1415 || !gst_is_video_overlay_prepare_window_handle_message(msg
))
1418 sink
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
1419 while (sink
!= ow
->sink
) {
1422 sink
= GST_ELEMENT_PARENT(sink
);
1425 g_signal_handlers_disconnect_matched(bus
, G_SIGNAL_MATCH_FUNC
1426 | G_SIGNAL_MATCH_DATA
, 0, 0, NULL
,
1429 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg
)),
1435 purple_media_manager_create_output_window(PurpleMediaManager
*manager
,
1436 PurpleMedia
*media
, const gchar
*session_id
,
1437 const gchar
*participant
)
1442 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1444 iter
= manager
->priv
->output_windows
;
1445 for(; iter
; iter
= g_list_next(iter
)) {
1446 PurpleMediaOutputWindow
*ow
= iter
->data
;
1448 if (ow
->sink
== NULL
&& ow
->media
== media
&&
1449 purple_strequal(participant
, ow
->participant
) &&
1450 purple_strequal(session_id
, ow
->session_id
)) {
1452 GstElement
*queue
, *convert
, *scale
;
1453 GstElement
*tee
= purple_media_get_tee(media
,
1454 session_id
, participant
);
1459 queue
= gst_element_factory_make("queue", NULL
);
1460 convert
= gst_element_factory_make("videoconvert", NULL
);
1461 scale
= gst_element_factory_make("videoscale", NULL
);
1462 ow
->sink
= purple_media_manager_get_element(
1463 manager
, PURPLE_MEDIA_RECV_VIDEO
,
1464 ow
->media
, ow
->session_id
,
1467 if (participant
== NULL
) {
1468 /* aka this is a preview sink */
1469 GObjectClass
*klass
=
1470 G_OBJECT_GET_CLASS(ow
->sink
);
1471 if (g_object_class_find_property(klass
,
1473 g_object_set(G_OBJECT(ow
->sink
),
1474 "sync", FALSE
, NULL
);
1475 if (g_object_class_find_property(klass
,
1477 g_object_set(G_OBJECT(ow
->sink
),
1478 "async", FALSE
, NULL
);
1481 gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee
)),
1482 queue
, convert
, scale
, ow
->sink
, NULL
);
1484 bus
= gst_pipeline_get_bus(GST_PIPELINE(
1485 manager
->priv
->pipeline
));
1486 g_signal_connect(bus
, "sync-message::element",
1487 G_CALLBACK(window_id_cb
), ow
);
1488 gst_object_unref(bus
);
1490 gst_element_set_state(ow
->sink
, GST_STATE_PLAYING
);
1491 gst_element_set_state(scale
, GST_STATE_PLAYING
);
1492 gst_element_set_state(convert
, GST_STATE_PLAYING
);
1493 gst_element_set_state(queue
, GST_STATE_PLAYING
);
1494 gst_element_link(scale
, ow
->sink
);
1495 gst_element_link(convert
, scale
);
1496 gst_element_link(queue
, convert
);
1497 gst_element_link(tee
, queue
);
1507 purple_media_manager_set_output_window(PurpleMediaManager
*manager
,
1508 PurpleMedia
*media
, const gchar
*session_id
,
1509 const gchar
*participant
, gulong window_id
)
1512 PurpleMediaOutputWindow
*output_window
;
1514 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1515 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1517 output_window
= g_new0(PurpleMediaOutputWindow
, 1);
1518 output_window
->id
= manager
->priv
->next_output_window_id
++;
1519 output_window
->media
= media
;
1520 output_window
->session_id
= g_strdup(session_id
);
1521 output_window
->participant
= g_strdup(participant
);
1522 output_window
->window_id
= window_id
;
1524 manager
->priv
->output_windows
= g_list_prepend(
1525 manager
->priv
->output_windows
, output_window
);
1527 if (purple_media_get_tee(media
, session_id
, participant
) != NULL
)
1528 purple_media_manager_create_output_window(manager
,
1529 media
, session_id
, participant
);
1531 return output_window
->id
;
1538 purple_media_manager_remove_output_window(PurpleMediaManager
*manager
,
1539 gulong output_window_id
)
1542 PurpleMediaOutputWindow
*output_window
= NULL
;
1545 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1547 iter
= manager
->priv
->output_windows
;
1548 for (; iter
; iter
= g_list_next(iter
)) {
1549 PurpleMediaOutputWindow
*ow
= iter
->data
;
1550 if (ow
->id
== output_window_id
) {
1551 manager
->priv
->output_windows
= g_list_delete_link(
1552 manager
->priv
->output_windows
, iter
);
1558 if (output_window
== NULL
)
1561 if (output_window
->sink
!= NULL
) {
1562 GstElement
*element
= output_window
->sink
;
1563 GstPad
*teepad
= NULL
;
1564 GSList
*to_remove
= NULL
;
1566 /* Find the tee element this output is connected to. */
1570 GstElementFactory
*factory
;
1571 const gchar
*factory_name
;
1573 to_remove
= g_slist_append(to_remove
, element
);
1575 pad
= gst_element_get_static_pad(element
, "sink");
1576 peer
= gst_pad_get_peer(pad
);
1578 /* Output is disconnected from the pipeline. */
1579 gst_object_unref(pad
);
1583 factory
= gst_element_get_factory(GST_PAD_PARENT(peer
));
1584 factory_name
= gst_plugin_feature_get_name(factory
);
1585 if (purple_strequal(factory_name
, "tee")) {
1589 element
= GST_PAD_PARENT(peer
);
1591 gst_object_unref(pad
);
1592 gst_object_unref(peer
);
1596 gst_element_release_request_pad(GST_PAD_PARENT(teepad
),
1601 GstElement
*element
= to_remove
->data
;
1603 gst_element_set_locked_state(element
, TRUE
);
1604 gst_element_set_state(element
, GST_STATE_NULL
);
1605 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(element
)),
1607 to_remove
= g_slist_delete_link(to_remove
, to_remove
);
1611 g_free(output_window
->session_id
);
1612 g_free(output_window
->participant
);
1613 g_free(output_window
);
1622 purple_media_manager_remove_output_windows(PurpleMediaManager
*manager
,
1623 PurpleMedia
*media
, const gchar
*session_id
,
1624 const gchar
*participant
)
1629 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1631 iter
= manager
->priv
->output_windows
;
1634 PurpleMediaOutputWindow
*ow
= iter
->data
;
1635 iter
= g_list_next(iter
);
1637 if (media
== ow
->media
&&
1638 purple_strequal(session_id
, ow
->session_id
) &&
1639 purple_strequal(participant
, ow
->participant
))
1640 purple_media_manager_remove_output_window(
1647 purple_media_manager_set_ui_caps(PurpleMediaManager
*manager
,
1648 PurpleMediaCaps caps
)
1651 PurpleMediaCaps oldcaps
;
1653 g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
));
1655 oldcaps
= manager
->priv
->ui_caps
;
1656 manager
->priv
->ui_caps
= caps
;
1658 if (caps
!= oldcaps
)
1659 g_signal_emit(manager
,
1660 purple_media_manager_signals
[UI_CAPS_CHANGED
],
1666 purple_media_manager_get_ui_caps(PurpleMediaManager
*manager
)
1669 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
),
1670 PURPLE_MEDIA_CAPS_NONE
);
1671 return manager
->priv
->ui_caps
;
1673 return PURPLE_MEDIA_CAPS_NONE
;
1678 purple_media_manager_set_backend_type(PurpleMediaManager
*manager
,
1682 g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
));
1684 manager
->priv
->backend_type
= backend_type
;
1689 purple_media_manager_get_backend_type(PurpleMediaManager
*manager
)
1692 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
),
1693 PURPLE_MEDIA_CAPS_NONE
);
1695 return manager
->priv
->backend_type
;
1702 purple_media_manager_set_application_data_callbacks(PurpleMediaManager
*manager
,
1703 PurpleMedia
*media
, const gchar
*session_id
,
1704 const gchar
*participant
, PurpleMediaAppDataCallbacks
*callbacks
,
1705 gpointer user_data
, GDestroyNotify notify
)
1707 #ifdef HAVE_MEDIA_APPLICATION
1708 PurpleMediaAppDataInfo
* info
= ensure_app_data_info_and_lock (manager
,
1709 media
, session_id
, participant
);
1712 info
->notify (info
->user_data
);
1714 if (info
->readable_cb_token
) {
1715 g_source_remove (info
->readable_timer_id
);
1716 info
->readable_cb_token
= 0;
1719 if (info
->writable_cb_token
) {
1720 g_source_remove (info
->writable_timer_id
);
1721 info
->writable_cb_token
= 0;
1725 info
->callbacks
= *callbacks
;
1727 info
->callbacks
.writable
= NULL
;
1728 info
->callbacks
.readable
= NULL
;
1730 info
->user_data
= user_data
;
1731 info
->notify
= notify
;
1733 call_appsrc_writable_locked (info
);
1734 if (info
->num_samples
> 0 || info
->current_sample
!= NULL
)
1735 call_appsink_readable_locked (info
);
1737 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1742 purple_media_manager_send_application_data (
1743 PurpleMediaManager
*manager
, PurpleMedia
*media
, const gchar
*session_id
,
1744 const gchar
*participant
, gpointer buffer
, guint size
, gboolean blocking
)
1746 #ifdef HAVE_MEDIA_APPLICATION
1747 PurpleMediaAppDataInfo
* info
= get_app_data_info_and_lock (manager
,
1748 media
, session_id
, participant
);
1750 if (info
&& info
->appsrc
&& info
->connected
) {
1751 GstBuffer
*gstbuffer
= gst_buffer_new_wrapped (g_memdup (buffer
, size
),
1753 GstAppSrc
*appsrc
= gst_object_ref (info
->appsrc
);
1755 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1756 if (gst_app_src_push_buffer (appsrc
, gstbuffer
) == GST_FLOW_OK
) {
1760 srcpad
= gst_element_get_static_pad (GST_ELEMENT (appsrc
),
1763 gst_pad_peer_query (srcpad
, gst_query_new_drain ());
1764 gst_object_unref (srcpad
);
1767 gst_object_unref (appsrc
);
1770 gst_object_unref (appsrc
);
1774 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1782 purple_media_manager_receive_application_data (
1783 PurpleMediaManager
*manager
, PurpleMedia
*media
, const gchar
*session_id
,
1784 const gchar
*participant
, gpointer buffer
, guint max_size
,
1787 #ifdef HAVE_MEDIA_APPLICATION
1788 PurpleMediaAppDataInfo
* info
= get_app_data_info_and_lock (manager
,
1789 media
, session_id
, participant
);
1790 guint bytes_read
= 0;
1793 /* If we are in a blocking read, we need to loop until max_size data
1794 * is read into the buffer, if we're not, then we need to read as much
1798 if (!info
->current_sample
&& info
->appsink
&& info
->num_samples
> 0) {
1799 info
->current_sample
= gst_app_sink_pull_sample (info
->appsink
);
1800 info
->sample_offset
= 0;
1801 if (info
->current_sample
)
1802 info
->num_samples
--;
1805 if (info
->current_sample
) {
1806 GstBuffer
*gstbuffer
= gst_sample_get_buffer (
1807 info
->current_sample
);
1811 guint bytes_to_copy
;
1813 gst_buffer_map (gstbuffer
, &mapinfo
, GST_MAP_READ
);
1814 /* We must copy only the data remaining in the buffer without
1815 * overflowing the buffer */
1816 bytes_to_copy
= max_size
- bytes_read
;
1817 if (bytes_to_copy
> mapinfo
.size
- info
->sample_offset
)
1818 bytes_to_copy
= mapinfo
.size
- info
->sample_offset
;
1819 memcpy ((guint8
*)buffer
+ bytes_read
,
1820 mapinfo
.data
+ info
->sample_offset
, bytes_to_copy
);
1822 gst_buffer_unmap (gstbuffer
, &mapinfo
);
1823 info
->sample_offset
+= bytes_to_copy
;
1824 bytes_read
+= bytes_to_copy
;
1825 if (info
->sample_offset
== mapinfo
.size
) {
1826 gst_sample_unref (info
->current_sample
);
1827 info
->current_sample
= NULL
;
1828 info
->sample_offset
= 0;
1831 /* In case there's no buffer in the sample (should never
1832 * happen), we need to at least unref it */
1833 gst_sample_unref (info
->current_sample
);
1834 info
->current_sample
= NULL
;
1835 info
->sample_offset
= 0;
1839 /* If blocking, wait until there's an available sample */
1840 while (bytes_read
< max_size
&& blocking
&&
1841 info
->current_sample
== NULL
&& info
->num_samples
== 0) {
1842 g_cond_wait (&info
->readable_cond
, &manager
->priv
->appdata_mutex
);
1844 /* We've been signaled, we need to unlock and regrab the info
1845 * struct to make sure nothing changed */
1846 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1847 info
= get_app_data_info_and_lock (manager
,
1848 media
, session_id
, participant
);
1849 if (info
== NULL
|| info
->appsink
== NULL
) {
1850 /* The session was destroyed while we were waiting, we
1851 * should return here */
1852 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1856 } while (bytes_read
< max_size
&&
1857 (blocking
|| info
->num_samples
> 0));
1859 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1862 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1872 videosink_disable_last_sample(GstElement
*sink
)
1874 GObjectClass
*klass
= G_OBJECT_GET_CLASS(sink
);
1876 if (g_object_class_find_property(klass
, "enable-last-sample")) {
1877 g_object_set(sink
, "enable-last-sample", FALSE
, NULL
);
1881 #if GST_CHECK_VERSION(1, 4, 0)
1883 static PurpleMediaElementType
1884 gst_class_to_purple_element_type(const gchar
*device_class
)
1886 if (purple_strequal(device_class
, "Audio/Source")) {
1887 return PURPLE_MEDIA_ELEMENT_AUDIO
1888 | PURPLE_MEDIA_ELEMENT_SRC
1889 | PURPLE_MEDIA_ELEMENT_ONE_SRC
1890 | PURPLE_MEDIA_ELEMENT_UNIQUE
;
1891 } else if (purple_strequal(device_class
, "Audio/Sink")) {
1892 return PURPLE_MEDIA_ELEMENT_AUDIO
1893 | PURPLE_MEDIA_ELEMENT_SINK
1894 | PURPLE_MEDIA_ELEMENT_ONE_SINK
;
1895 } else if (purple_strequal(device_class
, "Video/Source")) {
1896 return PURPLE_MEDIA_ELEMENT_VIDEO
1897 | PURPLE_MEDIA_ELEMENT_SRC
1898 | PURPLE_MEDIA_ELEMENT_ONE_SRC
1899 | PURPLE_MEDIA_ELEMENT_UNIQUE
;
1900 } else if (purple_strequal(device_class
, "Video/Sink")) {
1901 return PURPLE_MEDIA_ELEMENT_VIDEO
1902 | PURPLE_MEDIA_ELEMENT_SINK
1903 | PURPLE_MEDIA_ELEMENT_ONE_SINK
;
1906 return PURPLE_MEDIA_ELEMENT_NONE
;
1910 gst_device_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
1911 const gchar
*session_id
, const gchar
*participant
)
1915 PurpleMediaElementType type
;
1917 device
= g_object_get_data(G_OBJECT(info
), "gst-device");
1922 result
= gst_device_create_element(device
, NULL
);
1927 type
= purple_media_element_info_get_element_type(info
);
1929 if ((type
& PURPLE_MEDIA_ELEMENT_VIDEO
) &&
1930 (type
& PURPLE_MEDIA_ELEMENT_SINK
)) {
1931 videosink_disable_last_sample(result
);
1938 device_is_ignored(GstDevice
*device
)
1940 gboolean result
= FALSE
;
1942 #if GST_CHECK_VERSION(1, 6, 0)
1943 gchar
*device_class
;
1945 g_return_val_if_fail(device
, TRUE
);
1947 device_class
= gst_device_get_device_class(device
);
1949 /* Ignore PulseAudio monitor audio sources since they have little use
1950 * in the context of telephony.*/
1951 if (purple_strequal(device_class
, "Audio/Source")) {
1952 GstStructure
*properties
;
1953 const gchar
*pa_class
;
1955 properties
= gst_device_get_properties(device
);
1957 pa_class
= gst_structure_get_string(properties
, "device.class");
1958 if (purple_strequal(pa_class
, "monitor")) {
1962 gst_structure_free(properties
);
1965 g_free(device_class
);
1966 #endif /* GST_CHECK_VERSION(1, 6, 0) */
1972 purple_media_manager_register_gst_device(PurpleMediaManager
*manager
,
1975 PurpleMediaElementInfo
*info
;
1976 PurpleMediaElementType type
;
1978 gchar
*device_class
;
1981 if (device_is_ignored(device
)) {
1985 name
= gst_device_get_display_name(device
);
1986 device_class
= gst_device_get_device_class(device
);
1988 id
= g_strdup_printf("%s %s", device_class
, name
);
1990 type
= gst_class_to_purple_element_type(device_class
);
1992 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1996 "create-cb", gst_device_create_cb
,
1999 g_object_set_data(G_OBJECT(info
), "gst-device", device
);
2001 purple_media_manager_register_element(manager
, info
);
2003 purple_debug_info("mediamanager", "Registered %s device %s",
2004 device_class
, name
);
2007 g_free(device_class
);
2012 purple_media_manager_unregister_gst_device(PurpleMediaManager
*manager
,
2017 gchar
*device_class
;
2018 gboolean done
= FALSE
;
2020 name
= gst_device_get_display_name(device
);
2021 device_class
= gst_device_get_device_class(device
);
2023 for (i
= manager
->priv
->elements
; i
&& !done
; i
= i
->next
) {
2024 PurpleMediaElementInfo
*info
= i
->data
;
2027 device2
= g_object_get_data(G_OBJECT(info
), "gst-device");
2030 gchar
*device_class2
;
2032 name2
= gst_device_get_display_name(device2
);
2033 device_class2
= gst_device_get_device_class(device2
);
2035 if (purple_strequal(name
, name2
) &&
2036 purple_strequal(device_class
, device_class2
)) {
2039 id
= purple_media_element_info_get_id(info
);
2040 purple_media_manager_unregister_element(manager
,
2043 purple_debug_info("mediamanager",
2044 "Unregistered %s device %s",
2045 device_class
, name
);
2053 g_free(device_class2
);
2058 g_free(device_class
);
2062 device_monitor_bus_cb(GstBus
*bus
, GstMessage
*message
, gpointer user_data
)
2064 PurpleMediaManager
*manager
= user_data
;
2065 GstMessageType message_type
;
2068 message_type
= GST_MESSAGE_TYPE(message
);
2070 if (message_type
== GST_MESSAGE_DEVICE_ADDED
) {
2071 gst_message_parse_device_added(message
, &device
);
2072 purple_media_manager_register_gst_device(manager
, device
);
2073 } else if (message_type
== GST_MESSAGE_DEVICE_REMOVED
) {
2074 gst_message_parse_device_removed (message
, &device
);
2075 purple_media_manager_unregister_gst_device(manager
, device
);
2078 return G_SOURCE_CONTINUE
;
2081 #endif /* GST_CHECK_VERSION(1, 4, 0) */
2084 purple_media_manager_init_device_monitor(PurpleMediaManager
*manager
)
2086 #if GST_CHECK_VERSION(1, 4, 0)
2090 manager
->priv
->device_monitor
= gst_device_monitor_new();
2092 bus
= gst_device_monitor_get_bus(manager
->priv
->device_monitor
);
2093 gst_bus_add_watch (bus
, device_monitor_bus_cb
, manager
);
2094 gst_object_unref (bus
);
2096 /* This avoids warning in GStreamer logs about no filters set */
2097 gst_device_monitor_add_filter(manager
->priv
->device_monitor
, NULL
, NULL
);
2099 gst_device_monitor_start(manager
->priv
->device_monitor
);
2101 i
= gst_device_monitor_get_devices(manager
->priv
->device_monitor
);
2102 for (; i
; i
= g_list_delete_link(i
, i
)) {
2103 GstDevice
*device
= i
->data
;
2105 purple_media_manager_register_gst_device(manager
, device
);
2106 gst_object_unref(device
);
2108 #endif /* GST_CHECK_VERSION(1, 4, 0) */
2112 purple_media_manager_enumerate_elements(PurpleMediaManager
*manager
,
2113 PurpleMediaElementType type
)
2115 GList
*result
= NULL
;
2118 for (i
= manager
->priv
->elements
; i
; i
= i
->next
) {
2119 PurpleMediaElementInfo
*info
= i
->data
;
2120 PurpleMediaElementType type2
;
2122 type2
= purple_media_element_info_get_element_type(info
);
2124 if ((type2
& type
) == type
) {
2126 result
= g_list_prepend(result
, info
);
2134 gst_factory_make_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2135 const gchar
*session_id
, const gchar
*participant
)
2138 GstElement
*element
;
2140 id
= purple_media_element_info_get_id(info
);
2142 element
= gst_element_factory_make(id
, NULL
);
2150 autovideosink_child_added_cb (GstChildProxy
*child_proxy
, GObject
*object
,
2151 gchar
*name
, gpointer user_data
)
2153 videosink_disable_last_sample(GST_ELEMENT(object
));
2157 default_video_sink_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2158 const gchar
*session_id
, const gchar
*participant
)
2160 GstElement
*videosink
= gst_element_factory_make("autovideosink", NULL
);
2162 g_signal_connect(videosink
, "child-added",
2163 G_CALLBACK(autovideosink_child_added_cb
), NULL
);
2169 disabled_video_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2170 const gchar
*session_id
, const gchar
*participant
)
2172 GstElement
*src
= gst_element_factory_make("videotestsrc", NULL
);
2174 /* GST_VIDEO_TEST_SRC_BLACK */
2175 g_object_set(src
, "pattern", 2, NULL
);
2181 test_video_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2182 const gchar
*session_id
, const gchar
*participant
)
2184 GstElement
*src
= gst_element_factory_make("videotestsrc", NULL
);
2186 g_object_set(src
, "is-live", TRUE
, NULL
);
2192 purple_media_manager_register_static_elements(PurpleMediaManager
*manager
)
2194 static const gchar
*VIDEO_SINK_PLUGINS
[] = {
2195 /* "aasink", "AALib", Didn't work for me */
2196 "directdrawsink", "DirectDraw",
2197 "glimagesink", "OpenGL",
2198 "ximagesink", "X Window System",
2199 "xvimagesink", "X Window System (Xv)",
2202 const gchar
**sinks
= VIDEO_SINK_PLUGINS
;
2204 /* Default auto* elements. */
2206 purple_media_manager_register_element(manager
,
2207 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2208 "id", "autoaudiosrc",
2209 "name", N_("Default"),
2210 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2211 | PURPLE_MEDIA_ELEMENT_SRC
2212 | PURPLE_MEDIA_ELEMENT_ONE_SRC
2213 | PURPLE_MEDIA_ELEMENT_UNIQUE
,
2214 "create-cb", gst_factory_make_cb
,
2217 purple_media_manager_register_element(manager
,
2218 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2219 "id", "autoaudiosink",
2220 "name", N_("Default"),
2221 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2222 | PURPLE_MEDIA_ELEMENT_SINK
2223 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2224 "create-cb", gst_factory_make_cb
,
2227 purple_media_manager_register_element(manager
,
2228 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2229 "id", "autovideosrc",
2230 "name", N_("Default"),
2231 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2232 | PURPLE_MEDIA_ELEMENT_SRC
2233 | PURPLE_MEDIA_ELEMENT_ONE_SRC
2234 | PURPLE_MEDIA_ELEMENT_UNIQUE
,
2235 "create-cb", gst_factory_make_cb
,
2238 purple_media_manager_register_element(manager
,
2239 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2240 "id", "autovideosink",
2241 "name", N_("Default"),
2242 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2243 | PURPLE_MEDIA_ELEMENT_SINK
2244 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2245 "create-cb", default_video_sink_create_cb
,
2248 /* Special elements */
2250 purple_media_manager_register_element(manager
,
2251 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2252 "id", "audiotestsrc",
2253 /* Translators: This is a noun that refers to one
2254 * possible audio input device. The device can help the
2255 * user to check if her speakers or headphones have been
2256 * set up correctly for voice calling. */
2257 "name", N_("Test Sound"),
2258 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2259 | PURPLE_MEDIA_ELEMENT_SRC
2260 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2261 "create-cb", gst_factory_make_cb
,
2264 purple_media_manager_register_element(manager
,
2265 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2266 "id", "disabledvideosrc",
2267 "name", N_("Disabled"),
2268 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2269 | PURPLE_MEDIA_ELEMENT_SRC
2270 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2271 "create-cb", disabled_video_create_cb
,
2274 purple_media_manager_register_element(manager
,
2275 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2276 "id", "videotestsrc",
2277 /* Translators: This is a noun that refers to one
2278 * possible video input device. The device produces
2279 * a test "monoscope" image that can help the user check
2280 * the video output has been set up correctly without
2281 * needing a webcam connected to the computer. */
2282 "name", N_("Test Pattern"),
2283 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2284 | PURPLE_MEDIA_ELEMENT_SRC
2285 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2286 "create-cb", test_video_create_cb
,
2289 for (sinks
= VIDEO_SINK_PLUGINS
; sinks
[0]; sinks
+= 2) {
2290 GstElementFactory
*factory
;
2292 factory
= gst_element_factory_find(sinks
[0]);
2297 purple_media_manager_register_element(manager
,
2298 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2301 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2302 | PURPLE_MEDIA_ELEMENT_SINK
2303 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2304 "create-cb", gst_factory_make_cb
,
2307 gst_object_unref(factory
);
2312 * PurpleMediaElementType
2316 purple_media_element_type_get_type()
2318 static GType type
= 0;
2320 static const GFlagsValue values
[] = {
2321 { PURPLE_MEDIA_ELEMENT_NONE
,
2322 "PURPLE_MEDIA_ELEMENT_NONE", "none" },
2323 { PURPLE_MEDIA_ELEMENT_AUDIO
,
2324 "PURPLE_MEDIA_ELEMENT_AUDIO", "audio" },
2325 { PURPLE_MEDIA_ELEMENT_VIDEO
,
2326 "PURPLE_MEDIA_ELEMENT_VIDEO", "video" },
2327 { PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO
,
2328 "PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO",
2330 { PURPLE_MEDIA_ELEMENT_NO_SRCS
,
2331 "PURPLE_MEDIA_ELEMENT_NO_SRCS", "no-srcs" },
2332 { PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2333 "PURPLE_MEDIA_ELEMENT_ONE_SRC", "one-src" },
2334 { PURPLE_MEDIA_ELEMENT_MULTI_SRC
,
2335 "PURPLE_MEDIA_ELEMENT_MULTI_SRC",
2337 { PURPLE_MEDIA_ELEMENT_REQUEST_SRC
,
2338 "PURPLE_MEDIA_ELEMENT_REQUEST_SRC",
2340 { PURPLE_MEDIA_ELEMENT_NO_SINKS
,
2341 "PURPLE_MEDIA_ELEMENT_NO_SINKS", "no-sinks" },
2342 { PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2343 "PURPLE_MEDIA_ELEMENT_ONE_SINK", "one-sink" },
2344 { PURPLE_MEDIA_ELEMENT_MULTI_SINK
,
2345 "PURPLE_MEDIA_ELEMENT_MULTI_SINK",
2347 { PURPLE_MEDIA_ELEMENT_REQUEST_SINK
,
2348 "PURPLE_MEDIA_ELEMENT_REQUEST_SINK",
2350 { PURPLE_MEDIA_ELEMENT_UNIQUE
,
2351 "PURPLE_MEDIA_ELEMENT_UNIQUE", "unique" },
2352 { PURPLE_MEDIA_ELEMENT_SRC
,
2353 "PURPLE_MEDIA_ELEMENT_SRC", "src" },
2354 { PURPLE_MEDIA_ELEMENT_SINK
,
2355 "PURPLE_MEDIA_ELEMENT_SINK", "sink" },
2356 { PURPLE_MEDIA_ELEMENT_APPLICATION
,
2357 "PURPLE_MEDIA_ELEMENT_APPLICATION", "application" },
2360 type
= g_flags_register_static(
2361 "PurpleMediaElementType", values
);
2368 * PurpleMediaElementInfo
2371 struct _PurpleMediaElementInfoClass
2373 GObjectClass parent_class
;
2376 struct _PurpleMediaElementInfo
2382 struct _PurpleMediaElementInfoPrivate
2386 PurpleMediaElementType type
;
2387 PurpleMediaElementCreateCallback create
;
2398 G_DEFINE_TYPE_WITH_PRIVATE(PurpleMediaElementInfo
,
2399 purple_media_element_info
, G_TYPE_OBJECT
);
2402 purple_media_element_info_init(PurpleMediaElementInfo
*info
)
2404 PurpleMediaElementInfoPrivate
*priv
=
2405 purple_media_element_info_get_instance_private(info
);
2408 priv
->type
= PURPLE_MEDIA_ELEMENT_NONE
;
2409 priv
->create
= NULL
;
2413 purple_media_element_info_finalize(GObject
*info
)
2415 PurpleMediaElementInfoPrivate
*priv
=
2416 purple_media_element_info_get_instance_private(
2417 PURPLE_MEDIA_ELEMENT_INFO(info
));
2421 G_OBJECT_CLASS(purple_media_element_info_parent_class
)->finalize(info
);
2425 purple_media_element_info_set_property (GObject
*object
, guint prop_id
,
2426 const GValue
*value
, GParamSpec
*pspec
)
2428 PurpleMediaElementInfoPrivate
*priv
;
2429 g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object
));
2431 priv
= purple_media_element_info_get_instance_private(
2432 PURPLE_MEDIA_ELEMENT_INFO(object
));
2437 priv
->id
= g_value_dup_string(value
);
2441 priv
->name
= g_value_dup_string(value
);
2444 priv
->type
= g_value_get_flags(value
);
2447 case PROP_CREATE_CB
:
2448 priv
->create
= g_value_get_pointer(value
);
2451 G_OBJECT_WARN_INVALID_PROPERTY_ID(
2452 object
, prop_id
, pspec
);
2458 purple_media_element_info_get_property (GObject
*object
, guint prop_id
,
2459 GValue
*value
, GParamSpec
*pspec
)
2461 PurpleMediaElementInfoPrivate
*priv
;
2462 g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object
));
2464 priv
= purple_media_element_info_get_instance_private(
2465 PURPLE_MEDIA_ELEMENT_INFO(object
));
2469 g_value_set_string(value
, priv
->id
);
2472 g_value_set_string(value
, priv
->name
);
2475 g_value_set_flags(value
, priv
->type
);
2477 case PROP_CREATE_CB
:
2478 g_value_set_pointer(value
, priv
->create
);
2481 G_OBJECT_WARN_INVALID_PROPERTY_ID(
2482 object
, prop_id
, pspec
);
2488 purple_media_element_info_class_init(PurpleMediaElementInfoClass
*klass
)
2490 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
2492 gobject_class
->finalize
= purple_media_element_info_finalize
;
2493 gobject_class
->set_property
= purple_media_element_info_set_property
;
2494 gobject_class
->get_property
= purple_media_element_info_get_property
;
2496 g_object_class_install_property(gobject_class
, PROP_ID
,
2497 g_param_spec_string("id",
2499 "The unique identifier of the element.",
2501 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2502 G_PARAM_STATIC_STRINGS
));
2504 g_object_class_install_property(gobject_class
, PROP_NAME
,
2505 g_param_spec_string("name",
2507 "The friendly/display name of this element.",
2509 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2510 G_PARAM_STATIC_STRINGS
));
2512 g_object_class_install_property(gobject_class
, PROP_TYPE
,
2513 g_param_spec_flags("type",
2515 "The type of element this is.",
2516 PURPLE_TYPE_MEDIA_ELEMENT_TYPE
,
2517 PURPLE_MEDIA_ELEMENT_NONE
,
2518 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2519 G_PARAM_STATIC_STRINGS
));
2521 g_object_class_install_property(gobject_class
, PROP_CREATE_CB
,
2522 g_param_spec_pointer("create-cb",
2524 "The function called to create this element.",
2525 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2526 G_PARAM_STATIC_STRINGS
));
2530 purple_media_element_info_get_id(PurpleMediaElementInfo
*info
)
2534 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2535 g_object_get(info
, "id", &id
, NULL
);
2540 purple_media_element_info_get_name(PurpleMediaElementInfo
*info
)
2543 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2544 g_object_get(info
, "name", &name
, NULL
);
2548 PurpleMediaElementType
2549 purple_media_element_info_get_element_type(PurpleMediaElementInfo
*info
)
2551 PurpleMediaElementType type
;
2552 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
),
2553 PURPLE_MEDIA_ELEMENT_NONE
);
2554 g_object_get(info
, "type", &type
, NULL
);
2559 purple_media_element_info_call_create(PurpleMediaElementInfo
*info
,
2560 PurpleMedia
*media
, const gchar
*session_id
,
2561 const gchar
*participant
)
2563 PurpleMediaElementCreateCallback create
;
2564 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2565 g_object_get(info
, "create-cb", &create
, NULL
);
2567 return create(info
, media
, session_id
, participant
);