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
57 struct _PurpleMediaManagerPrivate
60 PurpleMediaCaps ui_caps
;
62 GList
*private_medias
;
64 GList
*output_windows
;
65 gulong next_output_window_id
;
69 PurpleMediaElementInfo
*video_src
;
70 PurpleMediaElementInfo
*video_sink
;
71 PurpleMediaElementInfo
*audio_src
;
72 PurpleMediaElementInfo
*audio_sink
;
74 #if GST_CHECK_VERSION(1, 4, 0)
75 GstDeviceMonitor
*device_monitor
;
76 #endif /* GST_CHECK_VERSION(1, 4, 0) */
78 #ifdef HAVE_MEDIA_APPLICATION
79 /* Application data streams */
80 GList
*appdata_info
; /* holds PurpleMediaAppDataInfo */
82 guint appdata_cb_token
; /* last used read/write callback token */
86 #ifdef HAVE_MEDIA_APPLICATION
92 PurpleMediaAppDataCallbacks callbacks
;
94 GDestroyNotify notify
;
98 GstSample
*current_sample
;
102 guint writable_cb_token
;
103 guint readable_cb_token
;
104 guint writable_timer_id
;
105 guint readable_timer_id
;
107 } PurpleMediaAppDataInfo
;
110 #define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate))
111 #define PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_ELEMENT_INFO, PurpleMediaElementInfoPrivate))
113 static void purple_media_manager_class_init (PurpleMediaManagerClass
*klass
);
114 static void purple_media_manager_init (PurpleMediaManager
*media
);
115 static void purple_media_manager_finalize (GObject
*object
);
116 #ifdef HAVE_MEDIA_APPLICATION
117 static void free_appdata_info_locked (PurpleMediaAppDataInfo
*info
);
119 static void purple_media_manager_init_device_monitor(PurpleMediaManager
*manager
);
120 static void purple_media_manager_register_static_elements(PurpleMediaManager
*manager
);
122 static GObjectClass
*parent_class
= NULL
;
133 static guint purple_media_manager_signals
[LAST_SIGNAL
] = {0};
136 purple_media_manager_get_type()
139 static GType type
= 0;
142 static const GTypeInfo info
= {
143 sizeof(PurpleMediaManagerClass
),
146 (GClassInitFunc
) purple_media_manager_class_init
,
149 sizeof(PurpleMediaManager
),
151 (GInstanceInitFunc
) purple_media_manager_init
,
154 type
= g_type_register_static(G_TYPE_OBJECT
, "PurpleMediaManager", &info
, 0);
164 purple_media_manager_class_init (PurpleMediaManagerClass
*klass
)
166 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
167 parent_class
= g_type_class_peek_parent(klass
);
169 gobject_class
->finalize
= purple_media_manager_finalize
;
171 purple_media_manager_signals
[INIT_MEDIA
] = g_signal_new ("init-media",
172 G_TYPE_FROM_CLASS (klass
),
175 G_TYPE_BOOLEAN
, 3, PURPLE_TYPE_MEDIA
,
176 G_TYPE_POINTER
, G_TYPE_STRING
);
178 purple_media_manager_signals
[INIT_PRIVATE_MEDIA
] =
179 g_signal_new ("init-private-media",
180 G_TYPE_FROM_CLASS (klass
),
183 G_TYPE_BOOLEAN
, 3, PURPLE_TYPE_MEDIA
,
184 G_TYPE_POINTER
, G_TYPE_STRING
);
186 purple_media_manager_signals
[UI_CAPS_CHANGED
] = g_signal_new ("ui-caps-changed",
187 G_TYPE_FROM_CLASS (klass
),
190 G_TYPE_NONE
, 2, PURPLE_MEDIA_TYPE_CAPS
,
191 PURPLE_MEDIA_TYPE_CAPS
);
193 purple_media_manager_signals
[ELEMENTS_CHANGED
] =
194 g_signal_new("elements-changed",
195 G_TYPE_FROM_CLASS(klass
),
196 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
200 g_type_class_add_private(klass
, sizeof(PurpleMediaManagerPrivate
));
204 purple_media_manager_init (PurpleMediaManager
*media
)
208 media
->priv
= PURPLE_MEDIA_MANAGER_GET_PRIVATE(media
);
209 media
->priv
->medias
= NULL
;
210 media
->priv
->private_medias
= NULL
;
211 media
->priv
->next_output_window_id
= 1;
212 media
->priv
->backend_type
= PURPLE_TYPE_MEDIA_BACKEND_FS2
;
213 #ifdef HAVE_MEDIA_APPLICATION
214 media
->priv
->appdata_info
= NULL
;
215 g_mutex_init (&media
->priv
->appdata_mutex
);
217 if (gst_init_check(NULL
, NULL
, &error
)) {
218 purple_media_manager_register_static_elements(media
);
219 purple_media_manager_init_device_monitor(media
);
221 purple_debug_error("mediamanager",
222 "GStreamer failed to initialize: %s.",
223 error
? error
->message
: "");
229 purple_prefs_add_none("/purple/media");
230 purple_prefs_add_none("/purple/media/audio");
231 purple_prefs_add_int("/purple/media/audio/silence_threshold", 5);
232 purple_prefs_add_none("/purple/media/audio/volume");
233 purple_prefs_add_int("/purple/media/audio/volume/input", 10);
234 purple_prefs_add_int("/purple/media/audio/volume/output", 10);
238 purple_media_manager_finalize (GObject
*media
)
240 PurpleMediaManagerPrivate
*priv
= PURPLE_MEDIA_MANAGER_GET_PRIVATE(media
);
241 for (; priv
->medias
; priv
->medias
=
242 g_list_delete_link(priv
->medias
, priv
->medias
)) {
243 g_object_unref(priv
->medias
->data
);
245 for (; priv
->private_medias
; priv
->private_medias
=
246 g_list_delete_link(priv
->private_medias
, priv
->private_medias
)) {
247 g_object_unref(priv
->private_medias
->data
);
249 for (; priv
->elements
; priv
->elements
=
250 g_list_delete_link(priv
->elements
, priv
->elements
)) {
251 g_object_unref(priv
->elements
->data
);
253 if (priv
->video_caps
)
254 gst_caps_unref(priv
->video_caps
);
255 #ifdef HAVE_MEDIA_APPLICATION
256 if (priv
->appdata_info
)
257 g_list_free_full (priv
->appdata_info
,
258 (GDestroyNotify
) free_appdata_info_locked
);
259 g_mutex_clear (&priv
->appdata_mutex
);
261 #if GST_CHECK_VERSION(1, 4, 0)
262 if (priv
->device_monitor
) {
263 gst_device_monitor_stop(priv
->device_monitor
);
264 g_object_unref(priv
->device_monitor
);
266 #endif /* GST_CHECK_VERSION(1, 4, 0) */
268 parent_class
->finalize(media
);
273 purple_media_manager_get()
276 static PurpleMediaManager
*manager
= NULL
;
279 manager
= PURPLE_MEDIA_MANAGER(g_object_new(purple_media_manager_get_type(), NULL
));
288 pipeline_bus_call(GstBus
*bus
, GstMessage
*msg
, PurpleMediaManager
*manager
)
290 switch(GST_MESSAGE_TYPE(msg
)) {
291 case GST_MESSAGE_EOS
:
292 purple_debug_info("mediamanager", "End of Stream\n");
294 case GST_MESSAGE_ERROR
: {
298 gst_message_parse_error(msg
, &err
, &debug
);
300 purple_debug_error("mediamanager",
301 "gst pipeline error: %s\n",
306 purple_debug_error("mediamanager",
307 "Debug details: %s\n", debug
);
321 purple_media_manager_get_pipeline(PurpleMediaManager
*manager
)
323 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
325 if (manager
->priv
->pipeline
== NULL
) {
326 FsElementAddedNotifier
*notifier
;
331 manager
->priv
->pipeline
= gst_pipeline_new(NULL
);
333 bus
= gst_pipeline_get_bus(
334 GST_PIPELINE(manager
->priv
->pipeline
));
335 gst_bus_add_signal_watch(GST_BUS(bus
));
336 g_signal_connect(G_OBJECT(bus
), "message",
337 G_CALLBACK(pipeline_bus_call
), manager
);
338 gst_bus_set_sync_handler(bus
, gst_bus_sync_signal_handler
, NULL
, NULL
);
339 gst_object_unref(bus
);
341 filename
= g_build_filename(purple_user_dir(),
342 "fs-element.conf", NULL
);
343 keyfile
= g_key_file_new();
344 if (!g_key_file_load_from_file(keyfile
, filename
,
345 G_KEY_FILE_NONE
, &err
)) {
347 purple_debug_info("mediamanager",
349 "fs-element.conf: %s\n",
352 purple_debug_error("mediamanager",
354 "fs-element.conf: %s\n",
360 /* Hack to make alsasrc stop messing up audio timestamps */
361 if (!g_key_file_has_key(keyfile
,
362 "alsasrc", "slave-method", NULL
)) {
363 g_key_file_set_integer(keyfile
,
364 "alsasrc", "slave-method", 2);
367 notifier
= fs_element_added_notifier_new();
368 fs_element_added_notifier_add(notifier
,
369 GST_BIN(manager
->priv
->pipeline
));
370 fs_element_added_notifier_set_properties_from_keyfile(
373 gst_element_set_state(manager
->priv
->pipeline
,
377 return manager
->priv
->pipeline
;
382 create_media(PurpleMediaManager
*manager
,
383 PurpleAccount
*account
,
384 const char *conference_type
,
385 const char *remote_user
,
393 media
= PURPLE_MEDIA(g_object_new(purple_media_get_type(),
396 "conference-type", conference_type
,
397 "initiator", initiator
,
400 signal_id
= private ?
401 purple_media_manager_signals
[INIT_PRIVATE_MEDIA
] :
402 purple_media_manager_signals
[INIT_MEDIA
];
404 if (g_signal_has_handler_pending(manager
, signal_id
, 0, FALSE
)) {
407 g_signal_emit(manager
, signal_id
, 0, media
, account
, remote_user
,
409 if (signal_ret
== FALSE
) {
410 g_object_unref(media
);
416 manager
->priv
->private_medias
= g_list_append(
417 manager
->priv
->private_medias
, media
);
419 manager
->priv
->medias
= g_list_append(manager
->priv
->medias
, media
);
427 get_media(PurpleMediaManager
*manager
, gboolean
private)
431 return manager
->priv
->private_medias
;
433 return manager
->priv
->medias
;
440 get_media_by_account(PurpleMediaManager
*manager
,
441 PurpleAccount
*account
, gboolean
private)
447 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
450 iter
= manager
->priv
->private_medias
;
452 iter
= manager
->priv
->medias
;
453 for (; iter
; iter
= g_list_next(iter
)) {
454 if (purple_media_get_account(iter
->data
) == account
) {
455 media
= g_list_prepend(media
, iter
->data
);
466 purple_media_manager_remove_media(PurpleMediaManager
*manager
, PurpleMedia
*media
)
470 GList
**medias
= NULL
;
472 g_return_if_fail(manager
!= NULL
);
474 if ((list
= g_list_find(manager
->priv
->medias
, media
))) {
475 medias
= &manager
->priv
->medias
;
476 } else if ((list
= g_list_find(manager
->priv
->private_medias
, media
))) {
477 medias
= &manager
->priv
->private_medias
;
481 *medias
= g_list_delete_link(*medias
, list
);
483 #ifdef HAVE_MEDIA_APPLICATION
484 g_mutex_lock (&manager
->priv
->appdata_mutex
);
485 list
= manager
->priv
->appdata_info
;
487 PurpleMediaAppDataInfo
*info
= list
->data
;
488 GList
*next
= list
->next
;
490 if (info
->media
== media
) {
491 manager
->priv
->appdata_info
= g_list_delete_link (
492 manager
->priv
->appdata_info
, list
);
493 free_appdata_info_locked (info
);
498 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
505 purple_media_manager_create_media(PurpleMediaManager
*manager
,
506 PurpleAccount
*account
,
507 const char *conference_type
,
508 const char *remote_user
,
511 return create_media (manager
, account
, conference_type
,
512 remote_user
, initiator
, FALSE
);
516 purple_media_manager_get_media(PurpleMediaManager
*manager
)
518 return get_media (manager
, FALSE
);
522 purple_media_manager_get_media_by_account(PurpleMediaManager
*manager
,
523 PurpleAccount
*account
)
525 return get_media_by_account (manager
, account
, FALSE
);
529 purple_media_manager_create_private_media(PurpleMediaManager
*manager
,
530 PurpleAccount
*account
,
531 const char *conference_type
,
532 const char *remote_user
,
535 return create_media (manager
, account
, conference_type
,
536 remote_user
, initiator
, TRUE
);
540 purple_media_manager_get_private_media(PurpleMediaManager
*manager
)
542 return get_media (manager
, TRUE
);
546 purple_media_manager_get_private_media_by_account(PurpleMediaManager
*manager
,
547 PurpleAccount
*account
)
549 return get_media_by_account (manager
, account
, TRUE
);
552 #ifdef HAVE_MEDIA_APPLICATION
554 free_appdata_info_locked (PurpleMediaAppDataInfo
*info
)
556 GstAppSrcCallbacks null_src_cb
= { NULL
, NULL
, NULL
, { NULL
} };
557 GstAppSinkCallbacks null_sink_cb
= { NULL
, NULL
, NULL
, { NULL
} };
560 info
->notify (info
->user_data
);
564 /* Will call appsrc_destroyed. */
565 gst_app_src_set_callbacks (info
->appsrc
, &null_src_cb
,
569 /* Will call appsink_destroyed. */
570 gst_app_sink_set_callbacks (info
->appsink
, &null_sink_cb
,
574 /* Make sure no other thread is using the structure */
575 g_free (info
->session_id
);
576 g_free (info
->participant
);
578 /* This lets the potential read or write callbacks waiting for appdata_mutex
579 * know the info structure has been destroyed. */
580 info
->readable_cb_token
= 0;
581 info
->writable_cb_token
= 0;
583 if (info
->readable_timer_id
) {
584 g_source_remove (info
->readable_timer_id
);
585 info
->readable_timer_id
= 0;
588 if (info
->writable_timer_id
) {
589 g_source_remove (info
->writable_timer_id
);
590 info
->writable_timer_id
= 0;
593 if (info
->current_sample
)
594 gst_sample_unref (info
->current_sample
);
595 info
->current_sample
= NULL
;
597 /* Unblock any reading thread before destroying the GCond */
598 g_cond_broadcast (&info
->readable_cond
);
600 g_cond_clear (&info
->readable_cond
);
602 g_slice_free (PurpleMediaAppDataInfo
, info
);
606 * Get an app data info struct associated with a session and lock the mutex
607 * We don't want to return an info struct and unlock then it gets destroyed
608 * so we need to return it with the lock still taken
610 static PurpleMediaAppDataInfo
*
611 get_app_data_info_and_lock (PurpleMediaManager
*manager
,
612 PurpleMedia
*media
, const gchar
*session_id
, const gchar
*participant
)
616 g_mutex_lock (&manager
->priv
->appdata_mutex
);
617 for (i
= manager
->priv
->appdata_info
; i
; i
= i
->next
) {
618 PurpleMediaAppDataInfo
*info
= i
->data
;
620 if (info
->media
== media
&&
621 purple_strequal (info
->session_id
, session_id
) &&
622 (participant
== NULL
||
623 purple_strequal (info
->participant
, participant
))) {
632 * Get an app data info struct associated with a session and lock the mutex
633 * if it doesn't exist, we create it.
635 static PurpleMediaAppDataInfo
*
636 ensure_app_data_info_and_lock (PurpleMediaManager
*manager
, PurpleMedia
*media
,
637 const gchar
*session_id
, const gchar
*participant
)
639 PurpleMediaAppDataInfo
* info
= get_app_data_info_and_lock (manager
, media
,
640 session_id
, participant
);
643 info
= g_slice_new0 (PurpleMediaAppDataInfo
);
645 g_weak_ref_init (&info
->media_ref
, media
);
646 info
->session_id
= g_strdup (session_id
);
647 info
->participant
= g_strdup (participant
);
648 g_cond_init (&info
->readable_cond
);
649 manager
->priv
->appdata_info
= g_list_prepend (
650 manager
->priv
->appdata_info
, info
);
660 request_pad_unlinked_cb(GstPad
*pad
, GstPad
*peer
, gpointer user_data
)
662 GstElement
*parent
= GST_ELEMENT_PARENT(pad
);
664 GValue tmp
= G_VALUE_INIT
;
665 GstPad
*remaining_pad
;
666 GstIteratorResult result
;
668 gst_element_release_request_pad(parent
, pad
);
670 iter
= gst_element_iterate_src_pads(parent
);
672 result
= gst_iterator_next(iter
, &tmp
);
674 if (result
== GST_ITERATOR_DONE
) {
675 gst_element_set_locked_state(parent
, TRUE
);
676 gst_element_set_state(parent
, GST_STATE_NULL
);
677 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(parent
)), parent
);
678 } else if (result
== GST_ITERATOR_OK
) {
679 remaining_pad
= g_value_get_object(&tmp
);
681 gst_object_unref(remaining_pad
);
684 gst_iterator_free(iter
);
688 nonunique_src_unlinked_cb(GstPad
*pad
, GstPad
*peer
, gpointer user_data
)
690 GstElement
*element
= GST_ELEMENT_PARENT(pad
);
691 gst_element_set_locked_state(element
, TRUE
);
692 gst_element_set_state(element
, GST_STATE_NULL
);
693 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(element
)), element
);
697 purple_media_manager_set_video_caps(PurpleMediaManager
*manager
, GstCaps
*caps
)
699 if (manager
->priv
->video_caps
)
700 gst_caps_unref(manager
->priv
->video_caps
);
702 manager
->priv
->video_caps
= caps
;
704 if (manager
->priv
->pipeline
&& manager
->priv
->video_src
) {
705 gchar
*id
= purple_media_element_info_get_id(manager
->priv
->video_src
);
706 GstElement
*src
= gst_bin_get_by_name(GST_BIN(manager
->priv
->pipeline
), id
);
709 GstElement
*capsfilter
= gst_bin_get_by_name(GST_BIN(src
), "protocol_video_caps");
711 g_object_set(G_OBJECT(capsfilter
), "caps", caps
, NULL
);
712 gst_object_unref (capsfilter
);
714 gst_object_unref (src
);
722 purple_media_manager_get_video_caps(PurpleMediaManager
*manager
)
724 if (manager
->priv
->video_caps
== NULL
)
725 manager
->priv
->video_caps
= gst_caps_from_string("video/x-raw,"
726 "width=[250,352], height=[200,288], framerate=[1/1,20/1]");
727 return manager
->priv
->video_caps
;
731 #ifdef HAVE_MEDIA_APPLICATION
733 * Calls the appdata writable callback from the main thread.
734 * This needs to grab the appdata lock and make sure it didn't get destroyed
735 * before calling the callback.
738 appsrc_writable (gpointer user_data
)
740 PurpleMediaManager
*manager
= purple_media_manager_get ();
741 PurpleMediaAppDataInfo
*info
= user_data
;
742 void (*writable_cb
) (PurpleMediaManager
*manager
, PurpleMedia
*media
,
743 const gchar
*session_id
, const gchar
*participant
, gboolean writable
,
750 guint
*cb_token_ptr
= &info
->writable_cb_token
;
751 guint cb_token
= *cb_token_ptr
;
754 g_mutex_lock (&manager
->priv
->appdata_mutex
);
755 if (cb_token
== 0 || cb_token
!= *cb_token_ptr
) {
756 /* In case info was freed while we were waiting for the mutex to unlock
757 * we still have a pointer to the cb_token which should still be
758 * accessible since it's in the Glib slice allocator. It gets set to 0
759 * just after the timeout is canceled which happens also before the
760 * AppDataInfo is freed, so even if that memory slice gets reused, the
761 * cb_token would be different from its previous value (unless
762 * extremely unlucky). So checking if the value for the cb_token changed
763 * should be enough to prevent any kind of race condition in which the
764 * media/AppDataInfo gets destroyed in one thread while the timeout was
765 * triggered and is waiting on the mutex to get unlocked in this thread
767 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
770 writable_cb
= info
->callbacks
.writable
;
771 media
= g_weak_ref_get (&info
->media_ref
);
772 session_id
= g_strdup (info
->session_id
);
773 participant
= g_strdup (info
->participant
);
774 writable
= info
->writable
&& info
->connected
;
775 cb_data
= info
->user_data
;
777 info
->writable_cb_token
= 0;
778 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
781 if (writable_cb
&& media
)
782 writable_cb (manager
, media
, session_id
, participant
, writable
,
785 g_object_unref (media
);
787 g_free (participant
);
793 * Schedule a writable callback to be called from the main thread.
794 * We need to do this because need-data/enough-data signals from appsrc
795 * will come from the streaming thread and we need to create
796 * a source that we attach to the main context but we can't use
797 * g_main_context_invoke since we need to be able to cancel the source if the
798 * media gets destroyed.
799 * We use a timeout source instead of idle source, so the callback gets a higher
803 call_appsrc_writable_locked (PurpleMediaAppDataInfo
*info
)
805 PurpleMediaManager
*manager
= purple_media_manager_get ();
807 /* We already have a writable callback scheduled, don't create another one */
808 if (info
->writable_cb_token
|| info
->callbacks
.writable
== NULL
)
811 /* We can't use writable_timer_id as a token, because the timeout is added
812 * into libpurple's main event loop, which runs in a different thread than
813 * from where call_appsrc_writable_locked() was called. Consequently, the
814 * callback may run even before g_timeout_add() returns the timer ID
816 info
->writable_cb_token
= ++manager
->priv
->appdata_cb_token
;
817 info
->writable_timer_id
= g_timeout_add (0, appsrc_writable
, info
);
821 appsrc_need_data (GstAppSrc
*appsrc
, guint length
, gpointer user_data
)
823 PurpleMediaAppDataInfo
*info
= user_data
;
824 PurpleMediaManager
*manager
= purple_media_manager_get ();
826 g_mutex_lock (&manager
->priv
->appdata_mutex
);
827 if (!info
->writable
) {
828 info
->writable
= TRUE
;
829 /* Only signal writable if we also established a connection */
831 call_appsrc_writable_locked (info
);
833 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
837 appsrc_enough_data (GstAppSrc
*appsrc
, gpointer user_data
)
839 PurpleMediaAppDataInfo
*info
= user_data
;
840 PurpleMediaManager
*manager
= purple_media_manager_get ();
842 g_mutex_lock (&manager
->priv
->appdata_mutex
);
843 if (info
->writable
) {
844 info
->writable
= FALSE
;
845 call_appsrc_writable_locked (info
);
847 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
851 appsrc_seek_data (GstAppSrc
*appsrc
, guint64 offset
, gpointer user_data
)
857 appsrc_destroyed (PurpleMediaAppDataInfo
*info
)
859 PurpleMediaManager
*manager
;
862 /* PurpleMediaAppDataInfo is being freed. Return at once. */
866 manager
= purple_media_manager_get ();
868 g_mutex_lock (&manager
->priv
->appdata_mutex
);
870 if (info
->writable
) {
871 info
->writable
= FALSE
;
872 call_appsrc_writable_locked (info
);
874 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
878 media_established_cb (PurpleMedia
*media
,const gchar
*session_id
,
879 const gchar
*participant
, PurpleMediaCandidate
*local_candidate
,
880 PurpleMediaCandidate
*remote_candidate
, PurpleMediaAppDataInfo
*info
)
882 PurpleMediaManager
*manager
= purple_media_manager_get ();
884 g_mutex_lock (&manager
->priv
->appdata_mutex
);
885 info
->connected
= TRUE
;
886 /* We established the connection, if we were writable, then we need to
889 call_appsrc_writable_locked (info
);
890 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
894 create_send_appsrc(PurpleMediaElementInfo
*element_info
, PurpleMedia
*media
,
895 const gchar
*session_id
, const gchar
*participant
)
897 PurpleMediaManager
*manager
= purple_media_manager_get ();
898 PurpleMediaAppDataInfo
* info
= ensure_app_data_info_and_lock (manager
,
899 media
, session_id
, participant
);
900 GstElement
*appsrc
= (GstElement
*)info
->appsrc
;
902 if (appsrc
== NULL
) {
903 GstAppSrcCallbacks callbacks
= {appsrc_need_data
, appsrc_enough_data
,
904 appsrc_seek_data
, {NULL
}};
905 GstCaps
*caps
= gst_caps_new_empty_simple ("application/octet-stream");
907 appsrc
= gst_element_factory_make("appsrc", NULL
);
909 info
->appsrc
= (GstAppSrc
*)appsrc
;
911 gst_app_src_set_caps (info
->appsrc
, caps
);
912 gst_app_src_set_callbacks (info
->appsrc
,
913 &callbacks
, info
, (GDestroyNotify
) appsrc_destroyed
);
914 g_signal_connect (media
, "candidate-pair-established",
915 (GCallback
) media_established_cb
, info
);
916 gst_caps_unref (caps
);
919 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
924 appsink_eos (GstAppSink
*appsink
, gpointer user_data
)
929 appsink_new_preroll (GstAppSink
*appsink
, gpointer user_data
)
935 appsink_readable (gpointer user_data
)
937 PurpleMediaManager
*manager
= purple_media_manager_get ();
938 PurpleMediaAppDataInfo
*info
= user_data
;
939 void (*readable_cb
) (PurpleMediaManager
*manager
, PurpleMedia
*media
,
940 const gchar
*session_id
, const gchar
*participant
, gpointer user_data
);
945 guint
*cb_token_ptr
= &info
->readable_cb_token
;
946 guint cb_token
= *cb_token_ptr
;
947 gboolean run_again
= FALSE
;
949 g_mutex_lock (&manager
->priv
->appdata_mutex
);
950 if (cb_token
== 0 || cb_token
!= *cb_token_ptr
) {
951 /* Avoided a race condition (see writable callback) */
952 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
956 if (info
->callbacks
.readable
&&
957 (info
->num_samples
> 0 || info
->current_sample
!= NULL
)) {
958 readable_cb
= info
->callbacks
.readable
;
959 media
= g_weak_ref_get (&info
->media_ref
);
960 session_id
= g_strdup (info
->session_id
);
961 participant
= g_strdup (info
->participant
);
962 cb_data
= info
->user_data
;
963 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
966 readable_cb (manager
, media
, session_id
, participant
, cb_data
);
968 g_mutex_lock (&manager
->priv
->appdata_mutex
);
969 g_object_unref (media
);
971 g_free (participant
);
972 if (cb_token
== 0 || cb_token
!= *cb_token_ptr
) {
973 /* We got cancelled */
974 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
979 /* Do we still have samples? Schedule appsink_readable again. We break here
980 * so that other events get a chance to be processed too. */
981 if (info
->num_samples
> 0 || info
->current_sample
!= NULL
) {
984 info
->readable_cb_token
= 0;
987 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
992 call_appsink_readable_locked (PurpleMediaAppDataInfo
*info
)
994 PurpleMediaManager
*manager
= purple_media_manager_get ();
996 /* We must signal that a new sample has arrived to release blocking reads */
997 g_cond_broadcast (&info
->readable_cond
);
999 /* We already have a writable callback scheduled, don't create another one */
1000 if (info
->readable_cb_token
|| info
->callbacks
.readable
== NULL
)
1003 info
->readable_cb_token
= ++manager
->priv
->appdata_cb_token
;
1004 info
->readable_timer_id
= g_timeout_add (0, appsink_readable
, info
);
1007 static GstFlowReturn
1008 appsink_new_sample (GstAppSink
*appsink
, gpointer user_data
)
1010 PurpleMediaManager
*manager
= purple_media_manager_get ();
1011 PurpleMediaAppDataInfo
*info
= user_data
;
1013 g_mutex_lock (&manager
->priv
->appdata_mutex
);
1014 info
->num_samples
++;
1015 call_appsink_readable_locked (info
);
1016 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1022 appsink_destroyed (PurpleMediaAppDataInfo
*info
)
1024 PurpleMediaManager
*manager
;
1027 /* PurpleMediaAppDataInfo is being freed. Return at once. */
1031 manager
= purple_media_manager_get ();
1033 g_mutex_lock (&manager
->priv
->appdata_mutex
);
1034 info
->appsink
= NULL
;
1035 info
->num_samples
= 0;
1036 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1040 create_recv_appsink(PurpleMediaElementInfo
*element_info
, PurpleMedia
*media
,
1041 const gchar
*session_id
, const gchar
*participant
)
1043 PurpleMediaManager
*manager
= purple_media_manager_get ();
1044 PurpleMediaAppDataInfo
* info
= ensure_app_data_info_and_lock (manager
,
1045 media
, session_id
, participant
);
1046 GstElement
*appsink
= (GstElement
*)info
->appsink
;
1048 if (appsink
== NULL
) {
1049 GstAppSinkCallbacks callbacks
= {appsink_eos
, appsink_new_preroll
,
1050 appsink_new_sample
, {NULL
}};
1051 GstCaps
*caps
= gst_caps_new_empty_simple ("application/octet-stream");
1053 appsink
= gst_element_factory_make("appsink", NULL
);
1055 info
->appsink
= (GstAppSink
*)appsink
;
1057 gst_app_sink_set_caps (info
->appsink
, caps
);
1058 gst_app_sink_set_callbacks (info
->appsink
,
1059 &callbacks
, info
, (GDestroyNotify
) appsink_destroyed
);
1060 gst_caps_unref (caps
);
1064 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1067 #endif /* HAVE_MEDIA_APPLICATION */
1070 static PurpleMediaElementInfo
*
1071 get_send_application_element_info ()
1073 static PurpleMediaElementInfo
*info
= NULL
;
1075 #ifdef HAVE_MEDIA_APPLICATION
1077 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1078 "id", "pidginappsrc",
1079 "name", "Pidgin Application Source",
1080 "type", PURPLE_MEDIA_ELEMENT_APPLICATION
1081 | PURPLE_MEDIA_ELEMENT_SRC
1082 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
1083 "create-cb", create_send_appsrc
, NULL
);
1090 static PurpleMediaElementInfo
*
1091 get_recv_application_element_info ()
1093 static PurpleMediaElementInfo
*info
= NULL
;
1095 #ifdef HAVE_MEDIA_APPLICATION
1097 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1098 "id", "pidginappsink",
1099 "name", "Pidgin Application Sink",
1100 "type", PURPLE_MEDIA_ELEMENT_APPLICATION
1101 | PURPLE_MEDIA_ELEMENT_SINK
1102 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
1103 "create-cb", create_recv_appsink
, NULL
);
1111 purple_media_manager_get_element(PurpleMediaManager
*manager
,
1112 PurpleMediaSessionType type
, PurpleMedia
*media
,
1113 const gchar
*session_id
, const gchar
*participant
)
1115 GstElement
*ret
= NULL
;
1116 PurpleMediaElementInfo
*info
= NULL
;
1117 PurpleMediaElementType element_type
;
1119 if (type
& PURPLE_MEDIA_SEND_AUDIO
)
1120 info
= manager
->priv
->audio_src
;
1121 else if (type
& PURPLE_MEDIA_RECV_AUDIO
)
1122 info
= manager
->priv
->audio_sink
;
1123 else if (type
& PURPLE_MEDIA_SEND_VIDEO
)
1124 info
= manager
->priv
->video_src
;
1125 else if (type
& PURPLE_MEDIA_RECV_VIDEO
)
1126 info
= manager
->priv
->video_sink
;
1127 else if (type
& PURPLE_MEDIA_SEND_APPLICATION
)
1128 info
= get_send_application_element_info ();
1129 else if (type
& PURPLE_MEDIA_RECV_APPLICATION
)
1130 info
= get_recv_application_element_info ();
1135 element_type
= purple_media_element_info_get_element_type(info
);
1137 if (element_type
& PURPLE_MEDIA_ELEMENT_UNIQUE
&&
1138 element_type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1142 gchar
*id
= purple_media_element_info_get_id(info
);
1144 ret
= gst_bin_get_by_name(GST_BIN(
1145 purple_media_manager_get_pipeline(
1149 GstElement
*bin
, *fakesink
;
1150 ret
= purple_media_element_info_call_create(info
,
1151 media
, session_id
, participant
);
1152 bin
= gst_bin_new(id
);
1153 tee
= gst_element_factory_make("tee", "tee");
1154 gst_bin_add_many(GST_BIN(bin
), ret
, tee
, NULL
);
1156 if (type
& PURPLE_MEDIA_SEND_VIDEO
) {
1157 GstElement
*videoscale
;
1158 GstElement
*capsfilter
;
1160 videoscale
= gst_element_factory_make("videoscale", NULL
);
1161 capsfilter
= gst_element_factory_make("capsfilter", "protocol_video_caps");
1163 g_object_set(G_OBJECT(capsfilter
),
1164 "caps", purple_media_manager_get_video_caps(manager
), NULL
);
1166 gst_bin_add_many(GST_BIN(bin
), videoscale
, capsfilter
, NULL
);
1167 gst_element_link_many(ret
, videoscale
, capsfilter
, tee
, NULL
);
1169 gst_element_link(ret
, tee
);
1172 * This shouldn't be necessary, but it stops it from
1173 * giving a not-linked error upon destruction
1175 fakesink
= gst_element_factory_make("fakesink", NULL
);
1176 g_object_set(fakesink
,
1179 "enable-last-sample", FALSE
,
1181 gst_bin_add(GST_BIN(bin
), fakesink
);
1182 gst_element_link(tee
, fakesink
);
1185 gst_object_ref(ret
);
1186 gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(
1191 tee
= gst_bin_get_by_name(GST_BIN(ret
), "tee");
1192 pad
= gst_element_get_request_pad(tee
, "src_%u");
1193 gst_object_unref(tee
);
1194 ghost
= gst_ghost_pad_new(NULL
, pad
);
1195 gst_object_unref(pad
);
1196 g_signal_connect(GST_PAD(ghost
), "unlinked",
1197 G_CALLBACK(request_pad_unlinked_cb
), NULL
);
1198 gst_pad_set_active(ghost
, TRUE
);
1199 gst_element_add_pad(ret
, ghost
);
1201 ret
= purple_media_element_info_call_create(info
,
1202 media
, session_id
, participant
);
1203 if (element_type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1204 GstPad
*pad
= gst_element_get_static_pad(ret
, "src");
1205 g_signal_connect(pad
, "unlinked",
1206 G_CALLBACK(nonunique_src_unlinked_cb
), NULL
);
1207 gst_object_unref(pad
);
1208 gst_object_ref(ret
);
1209 gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(manager
)),
1215 purple_debug_error("media", "Error creating source or sink\n");
1220 PurpleMediaElementInfo
*
1221 purple_media_manager_get_element_info(PurpleMediaManager
*manager
,
1226 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
1227 g_return_val_if_fail(id
!= NULL
, NULL
);
1229 iter
= manager
->priv
->elements
;
1231 for (; iter
; iter
= g_list_next(iter
)) {
1233 purple_media_element_info_get_id(iter
->data
);
1234 if (purple_strequal(element_id
, id
)) {
1236 g_object_ref(iter
->data
);
1246 element_info_to_detail(PurpleMediaElementInfo
*info
)
1248 PurpleMediaElementType type
;
1250 type
= purple_media_element_info_get_element_type(info
);
1252 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1253 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1254 return g_quark_from_string("audiosrc");
1255 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1256 return g_quark_from_string("audiosink");
1258 } else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1259 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1260 return g_quark_from_string("videosrc");
1261 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1262 return g_quark_from_string("videosink");
1270 purple_media_manager_register_element(PurpleMediaManager
*manager
,
1271 PurpleMediaElementInfo
*info
)
1273 PurpleMediaElementInfo
*info2
;
1277 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1278 g_return_val_if_fail(info
!= NULL
, FALSE
);
1280 id
= purple_media_element_info_get_id(info
);
1281 info2
= purple_media_manager_get_element_info(manager
, id
);
1284 if (info2
!= NULL
) {
1285 g_object_unref(info2
);
1289 manager
->priv
->elements
=
1290 g_list_prepend(manager
->priv
->elements
, info
);
1292 detail
= element_info_to_detail(info
);
1294 g_signal_emit(manager
,
1295 purple_media_manager_signals
[ELEMENTS_CHANGED
],
1303 purple_media_manager_unregister_element(PurpleMediaManager
*manager
,
1306 PurpleMediaElementInfo
*info
;
1309 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1311 info
= purple_media_manager_get_element_info(manager
, id
);
1314 g_object_unref(info
);
1318 if (manager
->priv
->audio_src
== info
)
1319 manager
->priv
->audio_src
= NULL
;
1320 if (manager
->priv
->audio_sink
== info
)
1321 manager
->priv
->audio_sink
= NULL
;
1322 if (manager
->priv
->video_src
== info
)
1323 manager
->priv
->video_src
= NULL
;
1324 if (manager
->priv
->video_sink
== info
)
1325 manager
->priv
->video_sink
= NULL
;
1327 detail
= element_info_to_detail(info
);
1329 manager
->priv
->elements
= g_list_remove(
1330 manager
->priv
->elements
, info
);
1331 g_object_unref(info
);
1334 g_signal_emit(manager
,
1335 purple_media_manager_signals
[ELEMENTS_CHANGED
],
1343 purple_media_manager_set_active_element(PurpleMediaManager
*manager
,
1344 PurpleMediaElementInfo
*info
)
1346 PurpleMediaElementInfo
*info2
;
1347 PurpleMediaElementType type
;
1348 gboolean ret
= FALSE
;
1351 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1352 g_return_val_if_fail(info
!= NULL
, FALSE
);
1354 id
= purple_media_element_info_get_id(info
);
1355 info2
= purple_media_manager_get_element_info(manager
, id
);
1359 purple_media_manager_register_element(manager
, info
);
1361 g_object_unref(info2
);
1363 type
= purple_media_element_info_get_element_type(info
);
1365 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1366 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1367 manager
->priv
->audio_src
= info
;
1370 if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1371 manager
->priv
->video_src
= info
;
1375 if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1376 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1377 manager
->priv
->audio_sink
= info
;
1380 if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1381 manager
->priv
->video_sink
= info
;
1389 PurpleMediaElementInfo
*
1390 purple_media_manager_get_active_element(PurpleMediaManager
*manager
,
1391 PurpleMediaElementType type
)
1393 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
1395 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1396 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
)
1397 return manager
->priv
->audio_src
;
1398 else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
)
1399 return manager
->priv
->video_src
;
1400 else if (type
& PURPLE_MEDIA_ELEMENT_APPLICATION
)
1401 return get_send_application_element_info ();
1402 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1403 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
)
1404 return manager
->priv
->audio_sink
;
1405 else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
)
1406 return manager
->priv
->video_sink
;
1407 else if (type
& PURPLE_MEDIA_ELEMENT_APPLICATION
)
1408 return get_recv_application_element_info ();
1416 window_id_cb(GstBus
*bus
, GstMessage
*msg
, PurpleMediaOutputWindow
*ow
)
1420 if (GST_MESSAGE_TYPE(msg
) != GST_MESSAGE_ELEMENT
1421 || !gst_is_video_overlay_prepare_window_handle_message(msg
))
1424 sink
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
1425 while (sink
!= ow
->sink
) {
1428 sink
= GST_ELEMENT_PARENT(sink
);
1431 g_signal_handlers_disconnect_matched(bus
, G_SIGNAL_MATCH_FUNC
1432 | G_SIGNAL_MATCH_DATA
, 0, 0, NULL
,
1435 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg
)),
1441 purple_media_manager_create_output_window(PurpleMediaManager
*manager
,
1442 PurpleMedia
*media
, const gchar
*session_id
,
1443 const gchar
*participant
)
1448 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1450 iter
= manager
->priv
->output_windows
;
1451 for(; iter
; iter
= g_list_next(iter
)) {
1452 PurpleMediaOutputWindow
*ow
= iter
->data
;
1454 if (ow
->sink
== NULL
&& ow
->media
== media
&&
1455 purple_strequal(participant
, ow
->participant
) &&
1456 purple_strequal(session_id
, ow
->session_id
)) {
1458 GstElement
*queue
, *convert
, *scale
;
1459 GstElement
*tee
= purple_media_get_tee(media
,
1460 session_id
, participant
);
1465 queue
= gst_element_factory_make("queue", NULL
);
1466 convert
= gst_element_factory_make("videoconvert", NULL
);
1467 scale
= gst_element_factory_make("videoscale", NULL
);
1468 ow
->sink
= purple_media_manager_get_element(
1469 manager
, PURPLE_MEDIA_RECV_VIDEO
,
1470 ow
->media
, ow
->session_id
,
1473 if (participant
== NULL
) {
1474 /* aka this is a preview sink */
1475 GObjectClass
*klass
=
1476 G_OBJECT_GET_CLASS(ow
->sink
);
1477 if (g_object_class_find_property(klass
,
1479 g_object_set(G_OBJECT(ow
->sink
),
1480 "sync", FALSE
, NULL
);
1481 if (g_object_class_find_property(klass
,
1483 g_object_set(G_OBJECT(ow
->sink
),
1484 "async", FALSE
, NULL
);
1487 gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee
)),
1488 queue
, convert
, scale
, ow
->sink
, NULL
);
1490 bus
= gst_pipeline_get_bus(GST_PIPELINE(
1491 manager
->priv
->pipeline
));
1492 g_signal_connect(bus
, "sync-message::element",
1493 G_CALLBACK(window_id_cb
), ow
);
1494 gst_object_unref(bus
);
1496 gst_element_set_state(ow
->sink
, GST_STATE_PLAYING
);
1497 gst_element_set_state(scale
, GST_STATE_PLAYING
);
1498 gst_element_set_state(convert
, GST_STATE_PLAYING
);
1499 gst_element_set_state(queue
, GST_STATE_PLAYING
);
1500 gst_element_link(scale
, ow
->sink
);
1501 gst_element_link(convert
, scale
);
1502 gst_element_link(queue
, convert
);
1503 gst_element_link(tee
, queue
);
1513 purple_media_manager_set_output_window(PurpleMediaManager
*manager
,
1514 PurpleMedia
*media
, const gchar
*session_id
,
1515 const gchar
*participant
, gulong window_id
)
1518 PurpleMediaOutputWindow
*output_window
;
1520 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1521 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1523 output_window
= g_new0(PurpleMediaOutputWindow
, 1);
1524 output_window
->id
= manager
->priv
->next_output_window_id
++;
1525 output_window
->media
= media
;
1526 output_window
->session_id
= g_strdup(session_id
);
1527 output_window
->participant
= g_strdup(participant
);
1528 output_window
->window_id
= window_id
;
1530 manager
->priv
->output_windows
= g_list_prepend(
1531 manager
->priv
->output_windows
, output_window
);
1533 if (purple_media_get_tee(media
, session_id
, participant
) != NULL
)
1534 purple_media_manager_create_output_window(manager
,
1535 media
, session_id
, participant
);
1537 return output_window
->id
;
1544 purple_media_manager_remove_output_window(PurpleMediaManager
*manager
,
1545 gulong output_window_id
)
1548 PurpleMediaOutputWindow
*output_window
= NULL
;
1551 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1553 iter
= manager
->priv
->output_windows
;
1554 for (; iter
; iter
= g_list_next(iter
)) {
1555 PurpleMediaOutputWindow
*ow
= iter
->data
;
1556 if (ow
->id
== output_window_id
) {
1557 manager
->priv
->output_windows
= g_list_delete_link(
1558 manager
->priv
->output_windows
, iter
);
1564 if (output_window
== NULL
)
1567 if (output_window
->sink
!= NULL
) {
1568 GstElement
*element
= output_window
->sink
;
1569 GstPad
*teepad
= NULL
;
1570 GSList
*to_remove
= NULL
;
1572 /* Find the tee element this output is connected to. */
1576 GstElementFactory
*factory
;
1577 const gchar
*factory_name
;
1579 to_remove
= g_slist_append(to_remove
, element
);
1581 pad
= gst_element_get_static_pad(element
, "sink");
1582 peer
= gst_pad_get_peer(pad
);
1584 /* Output is disconnected from the pipeline. */
1585 gst_object_unref(pad
);
1589 factory
= gst_element_get_factory(GST_PAD_PARENT(peer
));
1590 factory_name
= gst_plugin_feature_get_name(factory
);
1591 if (purple_strequal(factory_name
, "tee")) {
1595 element
= GST_PAD_PARENT(peer
);
1597 gst_object_unref(pad
);
1598 gst_object_unref(peer
);
1602 gst_element_release_request_pad(GST_PAD_PARENT(teepad
),
1607 GstElement
*element
= to_remove
->data
;
1609 gst_element_set_locked_state(element
, TRUE
);
1610 gst_element_set_state(element
, GST_STATE_NULL
);
1611 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(element
)),
1613 to_remove
= g_slist_delete_link(to_remove
, to_remove
);
1617 g_free(output_window
->session_id
);
1618 g_free(output_window
->participant
);
1619 g_free(output_window
);
1628 purple_media_manager_remove_output_windows(PurpleMediaManager
*manager
,
1629 PurpleMedia
*media
, const gchar
*session_id
,
1630 const gchar
*participant
)
1635 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1637 iter
= manager
->priv
->output_windows
;
1640 PurpleMediaOutputWindow
*ow
= iter
->data
;
1641 iter
= g_list_next(iter
);
1643 if (media
== ow
->media
&&
1644 purple_strequal(session_id
, ow
->session_id
) &&
1645 purple_strequal(participant
, ow
->participant
))
1646 purple_media_manager_remove_output_window(
1653 purple_media_manager_set_ui_caps(PurpleMediaManager
*manager
,
1654 PurpleMediaCaps caps
)
1657 PurpleMediaCaps oldcaps
;
1659 g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
));
1661 oldcaps
= manager
->priv
->ui_caps
;
1662 manager
->priv
->ui_caps
= caps
;
1664 if (caps
!= oldcaps
)
1665 g_signal_emit(manager
,
1666 purple_media_manager_signals
[UI_CAPS_CHANGED
],
1672 purple_media_manager_get_ui_caps(PurpleMediaManager
*manager
)
1675 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
),
1676 PURPLE_MEDIA_CAPS_NONE
);
1677 return manager
->priv
->ui_caps
;
1679 return PURPLE_MEDIA_CAPS_NONE
;
1684 purple_media_manager_set_backend_type(PurpleMediaManager
*manager
,
1688 g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
));
1690 manager
->priv
->backend_type
= backend_type
;
1695 purple_media_manager_get_backend_type(PurpleMediaManager
*manager
)
1698 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
),
1699 PURPLE_MEDIA_CAPS_NONE
);
1701 return manager
->priv
->backend_type
;
1708 purple_media_manager_set_application_data_callbacks(PurpleMediaManager
*manager
,
1709 PurpleMedia
*media
, const gchar
*session_id
,
1710 const gchar
*participant
, PurpleMediaAppDataCallbacks
*callbacks
,
1711 gpointer user_data
, GDestroyNotify notify
)
1713 #ifdef HAVE_MEDIA_APPLICATION
1714 PurpleMediaAppDataInfo
* info
= ensure_app_data_info_and_lock (manager
,
1715 media
, session_id
, participant
);
1718 info
->notify (info
->user_data
);
1720 if (info
->readable_cb_token
) {
1721 g_source_remove (info
->readable_timer_id
);
1722 info
->readable_cb_token
= 0;
1725 if (info
->writable_cb_token
) {
1726 g_source_remove (info
->writable_timer_id
);
1727 info
->writable_cb_token
= 0;
1731 info
->callbacks
= *callbacks
;
1733 info
->callbacks
.writable
= NULL
;
1734 info
->callbacks
.readable
= NULL
;
1736 info
->user_data
= user_data
;
1737 info
->notify
= notify
;
1739 call_appsrc_writable_locked (info
);
1740 if (info
->num_samples
> 0 || info
->current_sample
!= NULL
)
1741 call_appsink_readable_locked (info
);
1743 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1748 purple_media_manager_send_application_data (
1749 PurpleMediaManager
*manager
, PurpleMedia
*media
, const gchar
*session_id
,
1750 const gchar
*participant
, gpointer buffer
, guint size
, gboolean blocking
)
1752 #ifdef HAVE_MEDIA_APPLICATION
1753 PurpleMediaAppDataInfo
* info
= get_app_data_info_and_lock (manager
,
1754 media
, session_id
, participant
);
1756 if (info
&& info
->appsrc
&& info
->connected
) {
1757 GstBuffer
*gstbuffer
= gst_buffer_new_wrapped (g_memdup (buffer
, size
),
1759 GstAppSrc
*appsrc
= gst_object_ref (info
->appsrc
);
1761 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1762 if (gst_app_src_push_buffer (appsrc
, gstbuffer
) == GST_FLOW_OK
) {
1766 srcpad
= gst_element_get_static_pad (GST_ELEMENT (appsrc
),
1769 gst_pad_peer_query (srcpad
, gst_query_new_drain ());
1770 gst_object_unref (srcpad
);
1773 gst_object_unref (appsrc
);
1776 gst_object_unref (appsrc
);
1780 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1788 purple_media_manager_receive_application_data (
1789 PurpleMediaManager
*manager
, PurpleMedia
*media
, const gchar
*session_id
,
1790 const gchar
*participant
, gpointer buffer
, guint max_size
,
1793 #ifdef HAVE_MEDIA_APPLICATION
1794 PurpleMediaAppDataInfo
* info
= get_app_data_info_and_lock (manager
,
1795 media
, session_id
, participant
);
1796 guint bytes_read
= 0;
1799 /* If we are in a blocking read, we need to loop until max_size data
1800 * is read into the buffer, if we're not, then we need to read as much
1804 if (!info
->current_sample
&& info
->appsink
&& info
->num_samples
> 0) {
1805 info
->current_sample
= gst_app_sink_pull_sample (info
->appsink
);
1806 info
->sample_offset
= 0;
1807 if (info
->current_sample
)
1808 info
->num_samples
--;
1811 if (info
->current_sample
) {
1812 GstBuffer
*gstbuffer
= gst_sample_get_buffer (
1813 info
->current_sample
);
1817 guint bytes_to_copy
;
1819 gst_buffer_map (gstbuffer
, &mapinfo
, GST_MAP_READ
);
1820 /* We must copy only the data remaining in the buffer without
1821 * overflowing the buffer */
1822 bytes_to_copy
= max_size
- bytes_read
;
1823 if (bytes_to_copy
> mapinfo
.size
- info
->sample_offset
)
1824 bytes_to_copy
= mapinfo
.size
- info
->sample_offset
;
1825 memcpy ((guint8
*)buffer
+ bytes_read
,
1826 mapinfo
.data
+ info
->sample_offset
, bytes_to_copy
);
1828 gst_buffer_unmap (gstbuffer
, &mapinfo
);
1829 info
->sample_offset
+= bytes_to_copy
;
1830 bytes_read
+= bytes_to_copy
;
1831 if (info
->sample_offset
== mapinfo
.size
) {
1832 gst_sample_unref (info
->current_sample
);
1833 info
->current_sample
= NULL
;
1834 info
->sample_offset
= 0;
1837 /* In case there's no buffer in the sample (should never
1838 * happen), we need to at least unref it */
1839 gst_sample_unref (info
->current_sample
);
1840 info
->current_sample
= NULL
;
1841 info
->sample_offset
= 0;
1845 /* If blocking, wait until there's an available sample */
1846 while (bytes_read
< max_size
&& blocking
&&
1847 info
->current_sample
== NULL
&& info
->num_samples
== 0) {
1848 g_cond_wait (&info
->readable_cond
, &manager
->priv
->appdata_mutex
);
1850 /* We've been signaled, we need to unlock and regrab the info
1851 * struct to make sure nothing changed */
1852 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1853 info
= get_app_data_info_and_lock (manager
,
1854 media
, session_id
, participant
);
1855 if (info
== NULL
|| info
->appsink
== NULL
) {
1856 /* The session was destroyed while we were waiting, we
1857 * should return here */
1858 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1862 } while (bytes_read
< max_size
&&
1863 (blocking
|| info
->num_samples
> 0));
1865 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1868 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1878 videosink_disable_last_sample(GstElement
*sink
)
1880 GObjectClass
*klass
= G_OBJECT_GET_CLASS(sink
);
1882 if (g_object_class_find_property(klass
, "enable-last-sample")) {
1883 g_object_set(sink
, "enable-last-sample", FALSE
, NULL
);
1887 #if GST_CHECK_VERSION(1, 4, 0)
1889 static PurpleMediaElementType
1890 gst_class_to_purple_element_type(const gchar
*device_class
)
1892 if (purple_strequal(device_class
, "Audio/Source")) {
1893 return PURPLE_MEDIA_ELEMENT_AUDIO
1894 | PURPLE_MEDIA_ELEMENT_SRC
1895 | PURPLE_MEDIA_ELEMENT_ONE_SRC
1896 | PURPLE_MEDIA_ELEMENT_UNIQUE
;
1897 } else if (purple_strequal(device_class
, "Audio/Sink")) {
1898 return PURPLE_MEDIA_ELEMENT_AUDIO
1899 | PURPLE_MEDIA_ELEMENT_SINK
1900 | PURPLE_MEDIA_ELEMENT_ONE_SINK
;
1901 } else if (purple_strequal(device_class
, "Video/Source")) {
1902 return PURPLE_MEDIA_ELEMENT_VIDEO
1903 | PURPLE_MEDIA_ELEMENT_SRC
1904 | PURPLE_MEDIA_ELEMENT_ONE_SRC
1905 | PURPLE_MEDIA_ELEMENT_UNIQUE
;
1906 } else if (purple_strequal(device_class
, "Video/Sink")) {
1907 return PURPLE_MEDIA_ELEMENT_VIDEO
1908 | PURPLE_MEDIA_ELEMENT_SINK
1909 | PURPLE_MEDIA_ELEMENT_ONE_SINK
;
1912 return PURPLE_MEDIA_ELEMENT_NONE
;
1916 gst_device_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
1917 const gchar
*session_id
, const gchar
*participant
)
1921 PurpleMediaElementType type
;
1923 device
= g_object_get_data(G_OBJECT(info
), "gst-device");
1928 result
= gst_device_create_element(device
, NULL
);
1933 type
= purple_media_element_info_get_element_type(info
);
1935 if ((type
& PURPLE_MEDIA_ELEMENT_VIDEO
) &&
1936 (type
& PURPLE_MEDIA_ELEMENT_SINK
)) {
1937 videosink_disable_last_sample(result
);
1944 device_is_ignored(GstDevice
*device
)
1946 gboolean result
= FALSE
;
1948 #if GST_CHECK_VERSION(1, 6, 0)
1949 gchar
*device_class
;
1951 g_return_val_if_fail(device
, TRUE
);
1953 device_class
= gst_device_get_device_class(device
);
1955 /* Ignore PulseAudio monitor audio sources since they have little use
1956 * in the context of telephony.*/
1957 if (purple_strequal(device_class
, "Audio/Source")) {
1958 GstStructure
*properties
;
1959 const gchar
*pa_class
;
1961 properties
= gst_device_get_properties(device
);
1963 pa_class
= gst_structure_get_string(properties
, "device.class");
1964 if (purple_strequal(pa_class
, "monitor")) {
1968 gst_structure_free(properties
);
1971 g_free(device_class
);
1972 #endif /* GST_CHECK_VERSION(1, 6, 0) */
1978 purple_media_manager_register_gst_device(PurpleMediaManager
*manager
,
1981 PurpleMediaElementInfo
*info
;
1982 PurpleMediaElementType type
;
1984 gchar
*device_class
;
1987 if (device_is_ignored(device
)) {
1991 name
= gst_device_get_display_name(device
);
1992 device_class
= gst_device_get_device_class(device
);
1994 id
= g_strdup_printf("%s %s", device_class
, name
);
1996 type
= gst_class_to_purple_element_type(device_class
);
1998 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2002 "create-cb", gst_device_create_cb
,
2005 g_object_set_data(G_OBJECT(info
), "gst-device", device
);
2007 purple_media_manager_register_element(manager
, info
);
2009 purple_debug_info("mediamanager", "Registered %s device %s",
2010 device_class
, name
);
2013 g_free(device_class
);
2018 purple_media_manager_unregister_gst_device(PurpleMediaManager
*manager
,
2023 gchar
*device_class
;
2024 gboolean done
= FALSE
;
2026 name
= gst_device_get_display_name(device
);
2027 device_class
= gst_device_get_device_class(device
);
2029 for (i
= manager
->priv
->elements
; i
&& !done
; i
= i
->next
) {
2030 PurpleMediaElementInfo
*info
= i
->data
;
2033 device2
= g_object_get_data(G_OBJECT(info
), "gst-device");
2036 gchar
*device_class2
;
2038 name2
= gst_device_get_display_name(device2
);
2039 device_class2
= gst_device_get_device_class(device2
);
2041 if (purple_strequal(name
, name2
) &&
2042 purple_strequal(device_class
, device_class2
)) {
2045 id
= purple_media_element_info_get_id(info
);
2046 purple_media_manager_unregister_element(manager
,
2049 purple_debug_info("mediamanager",
2050 "Unregistered %s device %s",
2051 device_class
, name
);
2059 g_free(device_class2
);
2064 g_free(device_class
);
2068 device_monitor_bus_cb(GstBus
*bus
, GstMessage
*message
, gpointer user_data
)
2070 PurpleMediaManager
*manager
= user_data
;
2071 GstMessageType message_type
;
2074 message_type
= GST_MESSAGE_TYPE(message
);
2076 if (message_type
== GST_MESSAGE_DEVICE_ADDED
) {
2077 gst_message_parse_device_added(message
, &device
);
2078 purple_media_manager_register_gst_device(manager
, device
);
2079 } else if (message_type
== GST_MESSAGE_DEVICE_REMOVED
) {
2080 gst_message_parse_device_removed (message
, &device
);
2081 purple_media_manager_unregister_gst_device(manager
, device
);
2084 return G_SOURCE_CONTINUE
;
2087 #endif /* GST_CHECK_VERSION(1, 4, 0) */
2090 purple_media_manager_init_device_monitor(PurpleMediaManager
*manager
)
2092 #if GST_CHECK_VERSION(1, 4, 0)
2096 manager
->priv
->device_monitor
= gst_device_monitor_new();
2098 bus
= gst_device_monitor_get_bus(manager
->priv
->device_monitor
);
2099 gst_bus_add_watch (bus
, device_monitor_bus_cb
, manager
);
2100 gst_object_unref (bus
);
2102 /* This avoids warning in GStreamer logs about no filters set */
2103 gst_device_monitor_add_filter(manager
->priv
->device_monitor
, NULL
, NULL
);
2105 gst_device_monitor_start(manager
->priv
->device_monitor
);
2107 i
= gst_device_monitor_get_devices(manager
->priv
->device_monitor
);
2108 for (; i
; i
= g_list_delete_link(i
, i
)) {
2109 GstDevice
*device
= i
->data
;
2111 purple_media_manager_register_gst_device(manager
, device
);
2112 gst_object_unref(device
);
2114 #endif /* GST_CHECK_VERSION(1, 4, 0) */
2118 purple_media_manager_enumerate_elements(PurpleMediaManager
*manager
,
2119 PurpleMediaElementType type
)
2121 GList
*result
= NULL
;
2124 for (i
= manager
->priv
->elements
; i
; i
= i
->next
) {
2125 PurpleMediaElementInfo
*info
= i
->data
;
2126 PurpleMediaElementType type2
;
2128 type2
= purple_media_element_info_get_element_type(info
);
2130 if ((type2
& type
) == type
) {
2132 result
= g_list_prepend(result
, info
);
2140 gst_factory_make_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2141 const gchar
*session_id
, const gchar
*participant
)
2144 GstElement
*element
;
2146 id
= purple_media_element_info_get_id(info
);
2148 element
= gst_element_factory_make(id
, NULL
);
2156 autovideosink_child_added_cb (GstChildProxy
*child_proxy
, GObject
*object
,
2157 gchar
*name
, gpointer user_data
)
2159 videosink_disable_last_sample(GST_ELEMENT(object
));
2163 default_video_sink_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2164 const gchar
*session_id
, const gchar
*participant
)
2166 GstElement
*videosink
= gst_element_factory_make("autovideosink", NULL
);
2168 g_signal_connect(videosink
, "child-added",
2169 G_CALLBACK(autovideosink_child_added_cb
), NULL
);
2175 disabled_video_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2176 const gchar
*session_id
, const gchar
*participant
)
2178 GstElement
*src
= gst_element_factory_make("videotestsrc", NULL
);
2180 /* GST_VIDEO_TEST_SRC_BLACK */
2181 g_object_set(src
, "pattern", 2, NULL
);
2187 test_video_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2188 const gchar
*session_id
, const gchar
*participant
)
2190 GstElement
*src
= gst_element_factory_make("videotestsrc", NULL
);
2192 g_object_set(src
, "is-live", TRUE
, NULL
);
2198 purple_media_manager_register_static_elements(PurpleMediaManager
*manager
)
2200 static const gchar
*VIDEO_SINK_PLUGINS
[] = {
2201 /* "aasink", "AALib", Didn't work for me */
2202 "directdrawsink", "DirectDraw",
2203 "glimagesink", "OpenGL",
2204 "ximagesink", "X Window System",
2205 "xvimagesink", "X Window System (Xv)",
2208 const gchar
**sinks
= VIDEO_SINK_PLUGINS
;
2210 /* Default auto* elements. */
2212 purple_media_manager_register_element(manager
,
2213 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2214 "id", "autoaudiosrc",
2215 "name", N_("Default"),
2216 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2217 | PURPLE_MEDIA_ELEMENT_SRC
2218 | PURPLE_MEDIA_ELEMENT_ONE_SRC
2219 | PURPLE_MEDIA_ELEMENT_UNIQUE
,
2220 "create-cb", gst_factory_make_cb
,
2223 purple_media_manager_register_element(manager
,
2224 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2225 "id", "autoaudiosink",
2226 "name", N_("Default"),
2227 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2228 | PURPLE_MEDIA_ELEMENT_SINK
2229 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2230 "create-cb", gst_factory_make_cb
,
2233 purple_media_manager_register_element(manager
,
2234 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2235 "id", "autovideosrc",
2236 "name", N_("Default"),
2237 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2238 | PURPLE_MEDIA_ELEMENT_SRC
2239 | PURPLE_MEDIA_ELEMENT_ONE_SRC
2240 | PURPLE_MEDIA_ELEMENT_UNIQUE
,
2241 "create-cb", gst_factory_make_cb
,
2244 purple_media_manager_register_element(manager
,
2245 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2246 "id", "autovideosink",
2247 "name", N_("Default"),
2248 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2249 | PURPLE_MEDIA_ELEMENT_SINK
2250 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2251 "create-cb", default_video_sink_create_cb
,
2254 /* Special elements */
2256 purple_media_manager_register_element(manager
,
2257 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2258 "id", "audiotestsrc",
2259 /* Translators: This is a noun that refers to one
2260 * possible audio input device. The device can help the
2261 * user to check if her speakers or headphones have been
2262 * set up correctly for voice calling. */
2263 "name", N_("Test Sound"),
2264 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2265 | PURPLE_MEDIA_ELEMENT_SRC
2266 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2267 "create-cb", gst_factory_make_cb
,
2270 purple_media_manager_register_element(manager
,
2271 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2272 "id", "disabledvideosrc",
2273 "name", N_("Disabled"),
2274 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2275 | PURPLE_MEDIA_ELEMENT_SRC
2276 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2277 "create-cb", disabled_video_create_cb
,
2280 purple_media_manager_register_element(manager
,
2281 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2282 "id", "videotestsrc",
2283 /* Translators: This is a noun that refers to one
2284 * possible video input device. The device produces
2285 * a test "monoscope" image that can help the user check
2286 * the video output has been set up correctly without
2287 * needing a webcam connected to the computer. */
2288 "name", N_("Test Pattern"),
2289 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2290 | PURPLE_MEDIA_ELEMENT_SRC
2291 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2292 "create-cb", test_video_create_cb
,
2295 for (sinks
= VIDEO_SINK_PLUGINS
; sinks
[0]; sinks
+= 2) {
2296 GstElementFactory
*factory
;
2298 factory
= gst_element_factory_find(sinks
[0]);
2303 purple_media_manager_register_element(manager
,
2304 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2307 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2308 | PURPLE_MEDIA_ELEMENT_SINK
2309 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2310 "create-cb", gst_factory_make_cb
,
2313 gst_object_unref(factory
);
2318 * PurpleMediaElementType
2322 purple_media_element_type_get_type()
2324 static GType type
= 0;
2326 static const GFlagsValue values
[] = {
2327 { PURPLE_MEDIA_ELEMENT_NONE
,
2328 "PURPLE_MEDIA_ELEMENT_NONE", "none" },
2329 { PURPLE_MEDIA_ELEMENT_AUDIO
,
2330 "PURPLE_MEDIA_ELEMENT_AUDIO", "audio" },
2331 { PURPLE_MEDIA_ELEMENT_VIDEO
,
2332 "PURPLE_MEDIA_ELEMENT_VIDEO", "video" },
2333 { PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO
,
2334 "PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO",
2336 { PURPLE_MEDIA_ELEMENT_NO_SRCS
,
2337 "PURPLE_MEDIA_ELEMENT_NO_SRCS", "no-srcs" },
2338 { PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2339 "PURPLE_MEDIA_ELEMENT_ONE_SRC", "one-src" },
2340 { PURPLE_MEDIA_ELEMENT_MULTI_SRC
,
2341 "PURPLE_MEDIA_ELEMENT_MULTI_SRC",
2343 { PURPLE_MEDIA_ELEMENT_REQUEST_SRC
,
2344 "PURPLE_MEDIA_ELEMENT_REQUEST_SRC",
2346 { PURPLE_MEDIA_ELEMENT_NO_SINKS
,
2347 "PURPLE_MEDIA_ELEMENT_NO_SINKS", "no-sinks" },
2348 { PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2349 "PURPLE_MEDIA_ELEMENT_ONE_SINK", "one-sink" },
2350 { PURPLE_MEDIA_ELEMENT_MULTI_SINK
,
2351 "PURPLE_MEDIA_ELEMENT_MULTI_SINK",
2353 { PURPLE_MEDIA_ELEMENT_REQUEST_SINK
,
2354 "PURPLE_MEDIA_ELEMENT_REQUEST_SINK",
2356 { PURPLE_MEDIA_ELEMENT_UNIQUE
,
2357 "PURPLE_MEDIA_ELEMENT_UNIQUE", "unique" },
2358 { PURPLE_MEDIA_ELEMENT_SRC
,
2359 "PURPLE_MEDIA_ELEMENT_SRC", "src" },
2360 { PURPLE_MEDIA_ELEMENT_SINK
,
2361 "PURPLE_MEDIA_ELEMENT_SINK", "sink" },
2362 { PURPLE_MEDIA_ELEMENT_APPLICATION
,
2363 "PURPLE_MEDIA_ELEMENT_APPLICATION", "application" },
2366 type
= g_flags_register_static(
2367 "PurpleMediaElementType", values
);
2374 * PurpleMediaElementInfo
2377 struct _PurpleMediaElementInfoClass
2379 GObjectClass parent_class
;
2382 struct _PurpleMediaElementInfo
2388 struct _PurpleMediaElementInfoPrivate
2392 PurpleMediaElementType type
;
2393 PurpleMediaElementCreateCallback create
;
2405 purple_media_element_info_init(PurpleMediaElementInfo
*info
)
2407 PurpleMediaElementInfoPrivate
*priv
=
2408 PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(info
);
2411 priv
->type
= PURPLE_MEDIA_ELEMENT_NONE
;
2412 priv
->create
= NULL
;
2416 purple_media_element_info_finalize(GObject
*info
)
2418 PurpleMediaElementInfoPrivate
*priv
=
2419 PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(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_PRIVATE(object
);
2436 priv
->id
= g_value_dup_string(value
);
2440 priv
->name
= g_value_dup_string(value
);
2443 priv
->type
= g_value_get_flags(value
);
2446 case PROP_CREATE_CB
:
2447 priv
->create
= g_value_get_pointer(value
);
2450 G_OBJECT_WARN_INVALID_PROPERTY_ID(
2451 object
, prop_id
, pspec
);
2457 purple_media_element_info_get_property (GObject
*object
, guint prop_id
,
2458 GValue
*value
, GParamSpec
*pspec
)
2460 PurpleMediaElementInfoPrivate
*priv
;
2461 g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object
));
2463 priv
= PURPLE_MEDIA_ELEMENT_INFO_GET_PRIVATE(object
);
2467 g_value_set_string(value
, priv
->id
);
2470 g_value_set_string(value
, priv
->name
);
2473 g_value_set_flags(value
, priv
->type
);
2475 case PROP_CREATE_CB
:
2476 g_value_set_pointer(value
, priv
->create
);
2479 G_OBJECT_WARN_INVALID_PROPERTY_ID(
2480 object
, prop_id
, pspec
);
2486 purple_media_element_info_class_init(PurpleMediaElementInfoClass
*klass
)
2488 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
2490 gobject_class
->finalize
= purple_media_element_info_finalize
;
2491 gobject_class
->set_property
= purple_media_element_info_set_property
;
2492 gobject_class
->get_property
= purple_media_element_info_get_property
;
2494 g_object_class_install_property(gobject_class
, PROP_ID
,
2495 g_param_spec_string("id",
2497 "The unique identifier of the element.",
2499 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2500 G_PARAM_STATIC_STRINGS
));
2502 g_object_class_install_property(gobject_class
, PROP_NAME
,
2503 g_param_spec_string("name",
2505 "The friendly/display name of this element.",
2507 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2508 G_PARAM_STATIC_STRINGS
));
2510 g_object_class_install_property(gobject_class
, PROP_TYPE
,
2511 g_param_spec_flags("type",
2513 "The type of element this is.",
2514 PURPLE_TYPE_MEDIA_ELEMENT_TYPE
,
2515 PURPLE_MEDIA_ELEMENT_NONE
,
2516 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2517 G_PARAM_STATIC_STRINGS
));
2519 g_object_class_install_property(gobject_class
, PROP_CREATE_CB
,
2520 g_param_spec_pointer("create-cb",
2522 "The function called to create this element.",
2523 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2524 G_PARAM_STATIC_STRINGS
));
2526 g_type_class_add_private(klass
, sizeof(PurpleMediaElementInfoPrivate
));
2529 G_DEFINE_TYPE(PurpleMediaElementInfo
,
2530 purple_media_element_info
, G_TYPE_OBJECT
);
2533 purple_media_element_info_get_id(PurpleMediaElementInfo
*info
)
2537 #if GLIB_CHECK_VERSION(2, 37, 3)
2538 /* Silence a warning. This could be anywhere below G_DEFINE_TYPE */
2539 (void)purple_media_element_info_get_instance_private
;
2542 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2543 g_object_get(info
, "id", &id
, NULL
);
2548 purple_media_element_info_get_name(PurpleMediaElementInfo
*info
)
2551 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2552 g_object_get(info
, "name", &name
, NULL
);
2556 PurpleMediaElementType
2557 purple_media_element_info_get_element_type(PurpleMediaElementInfo
*info
)
2559 PurpleMediaElementType type
;
2560 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
),
2561 PURPLE_MEDIA_ELEMENT_NONE
);
2562 g_object_get(info
, "type", &type
, NULL
);
2567 purple_media_element_info_call_create(PurpleMediaElementInfo
*info
,
2568 PurpleMedia
*media
, const gchar
*session_id
,
2569 const gchar
*participant
)
2571 PurpleMediaElementCreateCallback create
;
2572 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2573 g_object_get(info
, "create-cb", &create
, NULL
);
2575 return create(info
, media
, session_id
, participant
);