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 g_list_free_full(priv
->medias
, g_object_unref
);
237 g_list_free_full(priv
->private_medias
, g_object_unref
);
238 g_list_free_full(priv
->elements
, g_object_unref
);
239 if (priv
->video_caps
)
240 gst_caps_unref(priv
->video_caps
);
241 #ifdef HAVE_MEDIA_APPLICATION
242 if (priv
->appdata_info
)
243 g_list_free_full (priv
->appdata_info
,
244 (GDestroyNotify
) free_appdata_info_locked
);
245 g_mutex_clear (&priv
->appdata_mutex
);
247 #if GST_CHECK_VERSION(1, 4, 0)
248 if (priv
->device_monitor
) {
249 gst_device_monitor_stop(priv
->device_monitor
);
250 g_object_unref(priv
->device_monitor
);
252 #endif /* GST_CHECK_VERSION(1, 4, 0) */
254 G_OBJECT_CLASS(purple_media_manager_parent_class
)->finalize(media
);
259 purple_media_manager_get()
262 static PurpleMediaManager
*manager
= NULL
;
265 manager
= PURPLE_MEDIA_MANAGER(g_object_new(purple_media_manager_get_type(), NULL
));
274 pipeline_bus_call(GstBus
*bus
, GstMessage
*msg
, PurpleMediaManager
*manager
)
276 switch(GST_MESSAGE_TYPE(msg
)) {
277 case GST_MESSAGE_EOS
:
278 purple_debug_info("mediamanager", "End of Stream\n");
280 case GST_MESSAGE_ERROR
: {
284 gst_message_parse_error(msg
, &err
, &debug
);
286 purple_debug_error("mediamanager",
287 "gst pipeline error: %s\n",
292 purple_debug_error("mediamanager",
293 "Debug details: %s\n", debug
);
307 purple_media_manager_get_pipeline(PurpleMediaManager
*manager
)
309 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
311 if (manager
->priv
->pipeline
== NULL
) {
312 FsElementAddedNotifier
*notifier
;
317 manager
->priv
->pipeline
= gst_pipeline_new(NULL
);
319 bus
= gst_pipeline_get_bus(
320 GST_PIPELINE(manager
->priv
->pipeline
));
321 gst_bus_add_signal_watch(GST_BUS(bus
));
322 g_signal_connect(G_OBJECT(bus
), "message",
323 G_CALLBACK(pipeline_bus_call
), manager
);
324 gst_bus_set_sync_handler(bus
, gst_bus_sync_signal_handler
, NULL
, NULL
);
325 gst_object_unref(bus
);
327 filename
= g_build_filename(purple_config_dir(),
328 "fs-element.conf", NULL
);
329 keyfile
= g_key_file_new();
330 if (!g_key_file_load_from_file(keyfile
, filename
,
331 G_KEY_FILE_NONE
, &err
)) {
333 purple_debug_info("mediamanager",
335 "fs-element.conf: %s\n",
338 purple_debug_error("mediamanager",
340 "fs-element.conf: %s\n",
346 /* Hack to make alsasrc stop messing up audio timestamps */
347 if (!g_key_file_has_key(keyfile
,
348 "alsasrc", "slave-method", NULL
)) {
349 g_key_file_set_integer(keyfile
,
350 "alsasrc", "slave-method", 2);
353 notifier
= fs_element_added_notifier_new();
354 fs_element_added_notifier_add(notifier
,
355 GST_BIN(manager
->priv
->pipeline
));
356 fs_element_added_notifier_set_properties_from_keyfile(
359 gst_element_set_state(manager
->priv
->pipeline
,
363 return manager
->priv
->pipeline
;
368 create_media(PurpleMediaManager
*manager
,
369 PurpleAccount
*account
,
370 const char *conference_type
,
371 const char *remote_user
,
379 media
= PURPLE_MEDIA(g_object_new(purple_media_get_type(),
382 "conference-type", conference_type
,
383 "initiator", initiator
,
386 signal_id
= private ?
387 purple_media_manager_signals
[INIT_PRIVATE_MEDIA
] :
388 purple_media_manager_signals
[INIT_MEDIA
];
390 if (g_signal_has_handler_pending(manager
, signal_id
, 0, FALSE
)) {
393 g_signal_emit(manager
, signal_id
, 0, media
, account
, remote_user
,
395 if (signal_ret
== FALSE
) {
396 g_object_unref(media
);
402 manager
->priv
->private_medias
= g_list_append(
403 manager
->priv
->private_medias
, media
);
405 manager
->priv
->medias
= g_list_append(manager
->priv
->medias
, media
);
413 get_media(PurpleMediaManager
*manager
, gboolean
private)
417 return manager
->priv
->private_medias
;
419 return manager
->priv
->medias
;
426 get_media_by_account(PurpleMediaManager
*manager
,
427 PurpleAccount
*account
, gboolean
private)
433 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
436 iter
= manager
->priv
->private_medias
;
438 iter
= manager
->priv
->medias
;
439 for (; iter
; iter
= g_list_next(iter
)) {
440 if (purple_media_get_account(iter
->data
) == account
) {
441 media
= g_list_prepend(media
, iter
->data
);
452 purple_media_manager_remove_media(PurpleMediaManager
*manager
, PurpleMedia
*media
)
456 GList
**medias
= NULL
;
458 g_return_if_fail(manager
!= NULL
);
460 if ((list
= g_list_find(manager
->priv
->medias
, media
))) {
461 medias
= &manager
->priv
->medias
;
462 } else if ((list
= g_list_find(manager
->priv
->private_medias
, media
))) {
463 medias
= &manager
->priv
->private_medias
;
467 *medias
= g_list_delete_link(*medias
, list
);
469 #ifdef HAVE_MEDIA_APPLICATION
470 g_mutex_lock (&manager
->priv
->appdata_mutex
);
471 list
= manager
->priv
->appdata_info
;
473 PurpleMediaAppDataInfo
*info
= list
->data
;
474 GList
*next
= list
->next
;
476 if (info
->media
== media
) {
477 manager
->priv
->appdata_info
= g_list_delete_link (
478 manager
->priv
->appdata_info
, list
);
479 free_appdata_info_locked (info
);
484 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
491 purple_media_manager_create_media(PurpleMediaManager
*manager
,
492 PurpleAccount
*account
,
493 const char *conference_type
,
494 const char *remote_user
,
497 return create_media (manager
, account
, conference_type
,
498 remote_user
, initiator
, FALSE
);
502 purple_media_manager_get_media(PurpleMediaManager
*manager
)
504 return get_media (manager
, FALSE
);
508 purple_media_manager_get_media_by_account(PurpleMediaManager
*manager
,
509 PurpleAccount
*account
)
511 return get_media_by_account (manager
, account
, FALSE
);
515 purple_media_manager_create_private_media(PurpleMediaManager
*manager
,
516 PurpleAccount
*account
,
517 const char *conference_type
,
518 const char *remote_user
,
521 return create_media (manager
, account
, conference_type
,
522 remote_user
, initiator
, TRUE
);
526 purple_media_manager_get_private_media(PurpleMediaManager
*manager
)
528 return get_media (manager
, TRUE
);
532 purple_media_manager_get_private_media_by_account(PurpleMediaManager
*manager
,
533 PurpleAccount
*account
)
535 return get_media_by_account (manager
, account
, TRUE
);
538 #ifdef HAVE_MEDIA_APPLICATION
540 free_appdata_info_locked (PurpleMediaAppDataInfo
*info
)
542 GstAppSrcCallbacks null_src_cb
= { NULL
, NULL
, NULL
, { NULL
} };
543 GstAppSinkCallbacks null_sink_cb
= { NULL
, NULL
, NULL
, { NULL
} };
546 info
->notify (info
->user_data
);
550 /* Will call appsrc_destroyed. */
551 gst_app_src_set_callbacks (info
->appsrc
, &null_src_cb
,
555 /* Will call appsink_destroyed. */
556 gst_app_sink_set_callbacks (info
->appsink
, &null_sink_cb
,
560 /* Make sure no other thread is using the structure */
561 g_free (info
->session_id
);
562 g_free (info
->participant
);
564 /* This lets the potential read or write callbacks waiting for appdata_mutex
565 * know the info structure has been destroyed. */
566 info
->readable_cb_token
= 0;
567 info
->writable_cb_token
= 0;
569 if (info
->readable_timer_id
) {
570 g_source_remove (info
->readable_timer_id
);
571 info
->readable_timer_id
= 0;
574 if (info
->writable_timer_id
) {
575 g_source_remove (info
->writable_timer_id
);
576 info
->writable_timer_id
= 0;
579 if (info
->current_sample
)
580 gst_sample_unref (info
->current_sample
);
581 info
->current_sample
= NULL
;
583 /* Unblock any reading thread before destroying the GCond */
584 g_cond_broadcast (&info
->readable_cond
);
586 g_cond_clear (&info
->readable_cond
);
588 g_slice_free (PurpleMediaAppDataInfo
, info
);
592 * Get an app data info struct associated with a session and lock the mutex
593 * We don't want to return an info struct and unlock then it gets destroyed
594 * so we need to return it with the lock still taken
596 static PurpleMediaAppDataInfo
*
597 get_app_data_info_and_lock (PurpleMediaManager
*manager
,
598 PurpleMedia
*media
, const gchar
*session_id
, const gchar
*participant
)
602 g_mutex_lock (&manager
->priv
->appdata_mutex
);
603 for (i
= manager
->priv
->appdata_info
; i
; i
= i
->next
) {
604 PurpleMediaAppDataInfo
*info
= i
->data
;
606 if (info
->media
== media
&&
607 purple_strequal (info
->session_id
, session_id
) &&
608 (participant
== NULL
||
609 purple_strequal (info
->participant
, participant
))) {
618 * Get an app data info struct associated with a session and lock the mutex
619 * if it doesn't exist, we create it.
621 static PurpleMediaAppDataInfo
*
622 ensure_app_data_info_and_lock (PurpleMediaManager
*manager
, PurpleMedia
*media
,
623 const gchar
*session_id
, const gchar
*participant
)
625 PurpleMediaAppDataInfo
* info
= get_app_data_info_and_lock (manager
, media
,
626 session_id
, participant
);
629 info
= g_slice_new0 (PurpleMediaAppDataInfo
);
631 g_weak_ref_init (&info
->media_ref
, media
);
632 info
->session_id
= g_strdup (session_id
);
633 info
->participant
= g_strdup (participant
);
634 g_cond_init (&info
->readable_cond
);
635 manager
->priv
->appdata_info
= g_list_prepend (
636 manager
->priv
->appdata_info
, info
);
646 request_pad_unlinked_cb(GstPad
*pad
, GstPad
*peer
, gpointer user_data
)
648 GstElement
*parent
= GST_ELEMENT_PARENT(pad
);
650 GValue tmp
= G_VALUE_INIT
;
651 GstPad
*remaining_pad
;
652 GstIteratorResult result
;
654 gst_element_release_request_pad(parent
, pad
);
656 iter
= gst_element_iterate_src_pads(parent
);
658 result
= gst_iterator_next(iter
, &tmp
);
660 if (result
== GST_ITERATOR_DONE
) {
661 gst_element_set_locked_state(parent
, TRUE
);
662 gst_element_set_state(parent
, GST_STATE_NULL
);
663 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(parent
)), parent
);
664 } else if (result
== GST_ITERATOR_OK
) {
665 remaining_pad
= g_value_get_object(&tmp
);
667 gst_object_unref(remaining_pad
);
670 gst_iterator_free(iter
);
674 nonunique_src_unlinked_cb(GstPad
*pad
, GstPad
*peer
, gpointer user_data
)
676 GstElement
*element
= GST_ELEMENT_PARENT(pad
);
677 gst_element_set_locked_state(element
, TRUE
);
678 gst_element_set_state(element
, GST_STATE_NULL
);
679 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(element
)), element
);
683 purple_media_manager_set_video_caps(PurpleMediaManager
*manager
, GstCaps
*caps
)
685 if (manager
->priv
->video_caps
)
686 gst_caps_unref(manager
->priv
->video_caps
);
688 manager
->priv
->video_caps
= caps
;
690 if (manager
->priv
->pipeline
&& manager
->priv
->video_src
) {
691 gchar
*id
= purple_media_element_info_get_id(manager
->priv
->video_src
);
692 GstElement
*src
= gst_bin_get_by_name(GST_BIN(manager
->priv
->pipeline
), id
);
695 GstElement
*capsfilter
= gst_bin_get_by_name(GST_BIN(src
), "protocol_video_caps");
697 g_object_set(G_OBJECT(capsfilter
), "caps", caps
, NULL
);
698 gst_object_unref (capsfilter
);
700 gst_object_unref (src
);
708 purple_media_manager_get_video_caps(PurpleMediaManager
*manager
)
710 if (manager
->priv
->video_caps
== NULL
)
711 manager
->priv
->video_caps
= gst_caps_from_string("video/x-raw,"
712 "width=[250,352], height=[200,288], framerate=[1/1,20/1]");
713 return manager
->priv
->video_caps
;
717 #ifdef HAVE_MEDIA_APPLICATION
719 * Calls the appdata writable callback from the main thread.
720 * This needs to grab the appdata lock and make sure it didn't get destroyed
721 * before calling the callback.
724 appsrc_writable (gpointer user_data
)
726 PurpleMediaManager
*manager
= purple_media_manager_get ();
727 PurpleMediaAppDataInfo
*info
= user_data
;
728 void (*writable_cb
) (PurpleMediaManager
*manager
, PurpleMedia
*media
,
729 const gchar
*session_id
, const gchar
*participant
, gboolean writable
,
736 guint
*cb_token_ptr
= &info
->writable_cb_token
;
737 guint cb_token
= *cb_token_ptr
;
740 g_mutex_lock (&manager
->priv
->appdata_mutex
);
741 if (cb_token
== 0 || cb_token
!= *cb_token_ptr
) {
742 /* In case info was freed while we were waiting for the mutex to unlock
743 * we still have a pointer to the cb_token which should still be
744 * accessible since it's in the Glib slice allocator. It gets set to 0
745 * just after the timeout is canceled which happens also before the
746 * AppDataInfo is freed, so even if that memory slice gets reused, the
747 * cb_token would be different from its previous value (unless
748 * extremely unlucky). So checking if the value for the cb_token changed
749 * should be enough to prevent any kind of race condition in which the
750 * media/AppDataInfo gets destroyed in one thread while the timeout was
751 * triggered and is waiting on the mutex to get unlocked in this thread
753 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
756 writable_cb
= info
->callbacks
.writable
;
757 media
= g_weak_ref_get (&info
->media_ref
);
758 session_id
= g_strdup (info
->session_id
);
759 participant
= g_strdup (info
->participant
);
760 writable
= info
->writable
&& info
->connected
;
761 cb_data
= info
->user_data
;
763 info
->writable_cb_token
= 0;
764 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
767 if (writable_cb
&& media
)
768 writable_cb (manager
, media
, session_id
, participant
, writable
,
771 g_object_unref (media
);
773 g_free (participant
);
779 * Schedule a writable callback to be called from the main thread.
780 * We need to do this because need-data/enough-data signals from appsrc
781 * will come from the streaming thread and we need to create
782 * a source that we attach to the main context but we can't use
783 * g_main_context_invoke since we need to be able to cancel the source if the
784 * media gets destroyed.
785 * We use a timeout source instead of idle source, so the callback gets a higher
789 call_appsrc_writable_locked (PurpleMediaAppDataInfo
*info
)
791 PurpleMediaManager
*manager
= purple_media_manager_get ();
793 /* We already have a writable callback scheduled, don't create another one */
794 if (info
->writable_cb_token
|| info
->callbacks
.writable
== NULL
)
797 /* We can't use writable_timer_id as a token, because the timeout is added
798 * into libpurple's main event loop, which runs in a different thread than
799 * from where call_appsrc_writable_locked() was called. Consequently, the
800 * callback may run even before g_timeout_add() returns the timer ID
802 info
->writable_cb_token
= ++manager
->priv
->appdata_cb_token
;
803 info
->writable_timer_id
= g_timeout_add (0, appsrc_writable
, info
);
807 appsrc_need_data (GstAppSrc
*appsrc
, guint length
, gpointer user_data
)
809 PurpleMediaAppDataInfo
*info
= user_data
;
810 PurpleMediaManager
*manager
= purple_media_manager_get ();
812 g_mutex_lock (&manager
->priv
->appdata_mutex
);
813 if (!info
->writable
) {
814 info
->writable
= TRUE
;
815 /* Only signal writable if we also established a connection */
817 call_appsrc_writable_locked (info
);
819 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
823 appsrc_enough_data (GstAppSrc
*appsrc
, gpointer user_data
)
825 PurpleMediaAppDataInfo
*info
= user_data
;
826 PurpleMediaManager
*manager
= purple_media_manager_get ();
828 g_mutex_lock (&manager
->priv
->appdata_mutex
);
829 if (info
->writable
) {
830 info
->writable
= FALSE
;
831 call_appsrc_writable_locked (info
);
833 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
837 appsrc_seek_data (GstAppSrc
*appsrc
, guint64 offset
, gpointer user_data
)
843 appsrc_destroyed (PurpleMediaAppDataInfo
*info
)
845 PurpleMediaManager
*manager
;
848 /* PurpleMediaAppDataInfo is being freed. Return at once. */
852 manager
= purple_media_manager_get ();
854 g_mutex_lock (&manager
->priv
->appdata_mutex
);
856 if (info
->writable
) {
857 info
->writable
= FALSE
;
858 call_appsrc_writable_locked (info
);
860 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
864 media_established_cb (PurpleMedia
*media
,const gchar
*session_id
,
865 const gchar
*participant
, PurpleMediaCandidate
*local_candidate
,
866 PurpleMediaCandidate
*remote_candidate
, PurpleMediaAppDataInfo
*info
)
868 PurpleMediaManager
*manager
= purple_media_manager_get ();
870 g_mutex_lock (&manager
->priv
->appdata_mutex
);
871 info
->connected
= TRUE
;
872 /* We established the connection, if we were writable, then we need to
875 call_appsrc_writable_locked (info
);
876 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
880 create_send_appsrc(PurpleMediaElementInfo
*element_info
, PurpleMedia
*media
,
881 const gchar
*session_id
, const gchar
*participant
)
883 PurpleMediaManager
*manager
= purple_media_manager_get ();
884 PurpleMediaAppDataInfo
* info
= ensure_app_data_info_and_lock (manager
,
885 media
, session_id
, participant
);
886 GstElement
*appsrc
= (GstElement
*)info
->appsrc
;
888 if (appsrc
== NULL
) {
889 GstAppSrcCallbacks callbacks
= {appsrc_need_data
, appsrc_enough_data
,
890 appsrc_seek_data
, {NULL
}};
891 GstCaps
*caps
= gst_caps_new_empty_simple ("application/octet-stream");
893 appsrc
= gst_element_factory_make("appsrc", NULL
);
895 info
->appsrc
= (GstAppSrc
*)appsrc
;
897 gst_app_src_set_caps (info
->appsrc
, caps
);
898 gst_app_src_set_callbacks (info
->appsrc
,
899 &callbacks
, info
, (GDestroyNotify
) appsrc_destroyed
);
900 g_signal_connect (media
, "candidate-pair-established",
901 (GCallback
) media_established_cb
, info
);
902 gst_caps_unref (caps
);
905 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
910 appsink_eos (GstAppSink
*appsink
, gpointer user_data
)
915 appsink_new_preroll (GstAppSink
*appsink
, gpointer user_data
)
921 appsink_readable (gpointer user_data
)
923 PurpleMediaManager
*manager
= purple_media_manager_get ();
924 PurpleMediaAppDataInfo
*info
= user_data
;
925 void (*readable_cb
) (PurpleMediaManager
*manager
, PurpleMedia
*media
,
926 const gchar
*session_id
, const gchar
*participant
, gpointer user_data
);
931 guint
*cb_token_ptr
= &info
->readable_cb_token
;
932 guint cb_token
= *cb_token_ptr
;
933 gboolean run_again
= FALSE
;
935 g_mutex_lock (&manager
->priv
->appdata_mutex
);
936 if (cb_token
== 0 || cb_token
!= *cb_token_ptr
) {
937 /* Avoided a race condition (see writable callback) */
938 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
942 if (info
->callbacks
.readable
&&
943 (info
->num_samples
> 0 || info
->current_sample
!= NULL
)) {
944 readable_cb
= info
->callbacks
.readable
;
945 media
= g_weak_ref_get (&info
->media_ref
);
946 session_id
= g_strdup (info
->session_id
);
947 participant
= g_strdup (info
->participant
);
948 cb_data
= info
->user_data
;
949 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
951 readable_cb(manager
, media
, session_id
, participant
, cb_data
);
953 g_mutex_lock (&manager
->priv
->appdata_mutex
);
954 g_object_unref (media
);
956 g_free (participant
);
957 if (cb_token
!= *cb_token_ptr
) {
958 /* We got cancelled */
959 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
964 /* Do we still have samples? Schedule appsink_readable again. We break here
965 * so that other events get a chance to be processed too. */
966 if (info
->num_samples
> 0 || info
->current_sample
!= NULL
) {
969 info
->readable_cb_token
= 0;
972 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
977 call_appsink_readable_locked (PurpleMediaAppDataInfo
*info
)
979 PurpleMediaManager
*manager
= purple_media_manager_get ();
981 /* We must signal that a new sample has arrived to release blocking reads */
982 g_cond_broadcast (&info
->readable_cond
);
984 /* We already have a writable callback scheduled, don't create another one */
985 if (info
->readable_cb_token
|| info
->callbacks
.readable
== NULL
)
988 info
->readable_cb_token
= ++manager
->priv
->appdata_cb_token
;
989 info
->readable_timer_id
= g_timeout_add (0, appsink_readable
, info
);
993 appsink_new_sample (GstAppSink
*appsink
, gpointer user_data
)
995 PurpleMediaManager
*manager
= purple_media_manager_get ();
996 PurpleMediaAppDataInfo
*info
= user_data
;
998 g_mutex_lock (&manager
->priv
->appdata_mutex
);
1000 call_appsink_readable_locked (info
);
1001 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1007 appsink_destroyed (PurpleMediaAppDataInfo
*info
)
1009 PurpleMediaManager
*manager
;
1012 /* PurpleMediaAppDataInfo is being freed. Return at once. */
1016 manager
= purple_media_manager_get ();
1018 g_mutex_lock (&manager
->priv
->appdata_mutex
);
1019 info
->appsink
= NULL
;
1020 info
->num_samples
= 0;
1021 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1025 create_recv_appsink(PurpleMediaElementInfo
*element_info
, PurpleMedia
*media
,
1026 const gchar
*session_id
, const gchar
*participant
)
1028 PurpleMediaManager
*manager
= purple_media_manager_get ();
1029 PurpleMediaAppDataInfo
* info
= ensure_app_data_info_and_lock (manager
,
1030 media
, session_id
, participant
);
1031 GstElement
*appsink
= (GstElement
*)info
->appsink
;
1033 if (appsink
== NULL
) {
1034 GstAppSinkCallbacks callbacks
= {appsink_eos
, appsink_new_preroll
,
1035 appsink_new_sample
, {NULL
}};
1036 GstCaps
*caps
= gst_caps_new_empty_simple ("application/octet-stream");
1038 appsink
= gst_element_factory_make("appsink", NULL
);
1040 info
->appsink
= (GstAppSink
*)appsink
;
1042 gst_app_sink_set_caps (info
->appsink
, caps
);
1043 gst_app_sink_set_callbacks (info
->appsink
,
1044 &callbacks
, info
, (GDestroyNotify
) appsink_destroyed
);
1045 gst_caps_unref (caps
);
1049 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1052 #endif /* HAVE_MEDIA_APPLICATION */
1055 static PurpleMediaElementInfo
*
1056 get_send_application_element_info ()
1058 static PurpleMediaElementInfo
*info
= NULL
;
1060 #ifdef HAVE_MEDIA_APPLICATION
1062 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1063 "id", "pidginappsrc",
1064 "name", "Pidgin Application Source",
1065 "type", PURPLE_MEDIA_ELEMENT_APPLICATION
1066 | PURPLE_MEDIA_ELEMENT_SRC
1067 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
1068 "create-cb", create_send_appsrc
, NULL
);
1075 static PurpleMediaElementInfo
*
1076 get_recv_application_element_info ()
1078 static PurpleMediaElementInfo
*info
= NULL
;
1080 #ifdef HAVE_MEDIA_APPLICATION
1082 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1083 "id", "pidginappsink",
1084 "name", "Pidgin Application Sink",
1085 "type", PURPLE_MEDIA_ELEMENT_APPLICATION
1086 | PURPLE_MEDIA_ELEMENT_SINK
1087 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
1088 "create-cb", create_recv_appsink
, NULL
);
1096 purple_media_manager_get_element(PurpleMediaManager
*manager
,
1097 PurpleMediaSessionType type
, PurpleMedia
*media
,
1098 const gchar
*session_id
, const gchar
*participant
)
1100 GstElement
*ret
= NULL
;
1101 PurpleMediaElementInfo
*info
= NULL
;
1102 PurpleMediaElementType element_type
;
1104 if (type
& PURPLE_MEDIA_SEND_AUDIO
)
1105 info
= manager
->priv
->audio_src
;
1106 else if (type
& PURPLE_MEDIA_RECV_AUDIO
)
1107 info
= manager
->priv
->audio_sink
;
1108 else if (type
& PURPLE_MEDIA_SEND_VIDEO
)
1109 info
= manager
->priv
->video_src
;
1110 else if (type
& PURPLE_MEDIA_RECV_VIDEO
)
1111 info
= manager
->priv
->video_sink
;
1112 else if (type
& PURPLE_MEDIA_SEND_APPLICATION
)
1113 info
= get_send_application_element_info ();
1114 else if (type
& PURPLE_MEDIA_RECV_APPLICATION
)
1115 info
= get_recv_application_element_info ();
1120 element_type
= purple_media_element_info_get_element_type(info
);
1122 if (element_type
& PURPLE_MEDIA_ELEMENT_UNIQUE
&&
1123 element_type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1127 gchar
*id
= purple_media_element_info_get_id(info
);
1129 ret
= gst_bin_get_by_name(GST_BIN(
1130 purple_media_manager_get_pipeline(
1134 GstElement
*bin
, *fakesink
;
1135 ret
= purple_media_element_info_call_create(info
,
1136 media
, session_id
, participant
);
1137 bin
= gst_bin_new(id
);
1138 tee
= gst_element_factory_make("tee", "tee");
1139 gst_bin_add_many(GST_BIN(bin
), ret
, tee
, NULL
);
1141 if (type
& PURPLE_MEDIA_SEND_VIDEO
) {
1142 GstElement
*videoscale
;
1143 GstElement
*capsfilter
;
1145 videoscale
= gst_element_factory_make("videoscale", NULL
);
1146 capsfilter
= gst_element_factory_make("capsfilter", "protocol_video_caps");
1148 g_object_set(G_OBJECT(capsfilter
),
1149 "caps", purple_media_manager_get_video_caps(manager
), NULL
);
1151 gst_bin_add_many(GST_BIN(bin
), videoscale
, capsfilter
, NULL
);
1152 gst_element_link_many(ret
, videoscale
, capsfilter
, tee
, NULL
);
1154 gst_element_link(ret
, tee
);
1157 * This shouldn't be necessary, but it stops it from
1158 * giving a not-linked error upon destruction
1160 fakesink
= gst_element_factory_make("fakesink", NULL
);
1161 g_object_set(fakesink
,
1164 "enable-last-sample", FALSE
,
1166 gst_bin_add(GST_BIN(bin
), fakesink
);
1167 gst_element_link(tee
, fakesink
);
1170 gst_object_ref(ret
);
1171 gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(
1176 tee
= gst_bin_get_by_name(GST_BIN(ret
), "tee");
1177 pad
= gst_element_get_request_pad(tee
, "src_%u");
1178 gst_object_unref(tee
);
1179 ghost
= gst_ghost_pad_new(NULL
, pad
);
1180 gst_object_unref(pad
);
1181 g_signal_connect(GST_PAD(ghost
), "unlinked",
1182 G_CALLBACK(request_pad_unlinked_cb
), NULL
);
1183 gst_pad_set_active(ghost
, TRUE
);
1184 gst_element_add_pad(ret
, ghost
);
1186 ret
= purple_media_element_info_call_create(info
,
1187 media
, session_id
, participant
);
1188 if (element_type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1189 GstPad
*pad
= gst_element_get_static_pad(ret
, "src");
1190 g_signal_connect(pad
, "unlinked",
1191 G_CALLBACK(nonunique_src_unlinked_cb
), NULL
);
1192 gst_object_unref(pad
);
1193 gst_object_ref(ret
);
1194 gst_bin_add(GST_BIN(purple_media_manager_get_pipeline(manager
)),
1200 purple_debug_error("media", "Error creating source or sink\n");
1205 PurpleMediaElementInfo
*
1206 purple_media_manager_get_element_info(PurpleMediaManager
*manager
,
1211 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
1212 g_return_val_if_fail(id
!= NULL
, NULL
);
1214 iter
= manager
->priv
->elements
;
1216 for (; iter
; iter
= g_list_next(iter
)) {
1218 purple_media_element_info_get_id(iter
->data
);
1219 if (purple_strequal(element_id
, id
)) {
1221 g_object_ref(iter
->data
);
1231 element_info_to_detail(PurpleMediaElementInfo
*info
)
1233 PurpleMediaElementType type
;
1235 type
= purple_media_element_info_get_element_type(info
);
1237 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1238 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1239 return g_quark_from_string("audiosrc");
1240 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1241 return g_quark_from_string("audiosink");
1243 } else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1244 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1245 return g_quark_from_string("videosrc");
1246 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1247 return g_quark_from_string("videosink");
1255 purple_media_manager_register_element(PurpleMediaManager
*manager
,
1256 PurpleMediaElementInfo
*info
)
1258 PurpleMediaElementInfo
*info2
;
1262 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1263 g_return_val_if_fail(info
!= NULL
, FALSE
);
1265 id
= purple_media_element_info_get_id(info
);
1266 info2
= purple_media_manager_get_element_info(manager
, id
);
1269 if (info2
!= NULL
) {
1270 g_object_unref(info2
);
1274 manager
->priv
->elements
=
1275 g_list_prepend(manager
->priv
->elements
, info
);
1277 detail
= element_info_to_detail(info
);
1279 g_signal_emit(manager
,
1280 purple_media_manager_signals
[ELEMENTS_CHANGED
],
1288 purple_media_manager_unregister_element(PurpleMediaManager
*manager
,
1291 PurpleMediaElementInfo
*info
;
1294 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1296 info
= purple_media_manager_get_element_info(manager
, id
);
1299 g_object_unref(info
);
1303 if (manager
->priv
->audio_src
== info
)
1304 manager
->priv
->audio_src
= NULL
;
1305 if (manager
->priv
->audio_sink
== info
)
1306 manager
->priv
->audio_sink
= NULL
;
1307 if (manager
->priv
->video_src
== info
)
1308 manager
->priv
->video_src
= NULL
;
1309 if (manager
->priv
->video_sink
== info
)
1310 manager
->priv
->video_sink
= NULL
;
1312 detail
= element_info_to_detail(info
);
1314 manager
->priv
->elements
= g_list_remove(
1315 manager
->priv
->elements
, info
);
1316 g_object_unref(info
);
1319 g_signal_emit(manager
,
1320 purple_media_manager_signals
[ELEMENTS_CHANGED
],
1328 purple_media_manager_set_active_element(PurpleMediaManager
*manager
,
1329 PurpleMediaElementInfo
*info
)
1331 PurpleMediaElementInfo
*info2
;
1332 PurpleMediaElementType type
;
1333 gboolean ret
= FALSE
;
1336 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1337 g_return_val_if_fail(info
!= NULL
, FALSE
);
1339 id
= purple_media_element_info_get_id(info
);
1340 info2
= purple_media_manager_get_element_info(manager
, id
);
1344 purple_media_manager_register_element(manager
, info
);
1346 g_object_unref(info2
);
1348 type
= purple_media_element_info_get_element_type(info
);
1350 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1351 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1352 manager
->priv
->audio_src
= info
;
1355 if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1356 manager
->priv
->video_src
= info
;
1360 if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1361 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
) {
1362 manager
->priv
->audio_sink
= info
;
1365 if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
) {
1366 manager
->priv
->video_sink
= info
;
1374 PurpleMediaElementInfo
*
1375 purple_media_manager_get_active_element(PurpleMediaManager
*manager
,
1376 PurpleMediaElementType type
)
1378 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), NULL
);
1380 if (type
& PURPLE_MEDIA_ELEMENT_SRC
) {
1381 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
)
1382 return manager
->priv
->audio_src
;
1383 else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
)
1384 return manager
->priv
->video_src
;
1385 else if (type
& PURPLE_MEDIA_ELEMENT_APPLICATION
)
1386 return get_send_application_element_info ();
1387 } else if (type
& PURPLE_MEDIA_ELEMENT_SINK
) {
1388 if (type
& PURPLE_MEDIA_ELEMENT_AUDIO
)
1389 return manager
->priv
->audio_sink
;
1390 else if (type
& PURPLE_MEDIA_ELEMENT_VIDEO
)
1391 return manager
->priv
->video_sink
;
1392 else if (type
& PURPLE_MEDIA_ELEMENT_APPLICATION
)
1393 return get_recv_application_element_info ();
1401 window_id_cb(GstBus
*bus
, GstMessage
*msg
, PurpleMediaOutputWindow
*ow
)
1405 if (GST_MESSAGE_TYPE(msg
) != GST_MESSAGE_ELEMENT
1406 || !gst_is_video_overlay_prepare_window_handle_message(msg
))
1409 sink
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
1410 while (sink
!= ow
->sink
) {
1413 sink
= GST_ELEMENT_PARENT(sink
);
1416 g_signal_handlers_disconnect_matched(bus
, G_SIGNAL_MATCH_FUNC
1417 | G_SIGNAL_MATCH_DATA
, 0, 0, NULL
,
1420 gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg
)),
1426 purple_media_manager_create_output_window(PurpleMediaManager
*manager
,
1427 PurpleMedia
*media
, const gchar
*session_id
,
1428 const gchar
*participant
)
1433 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1435 iter
= manager
->priv
->output_windows
;
1436 for(; iter
; iter
= g_list_next(iter
)) {
1437 PurpleMediaOutputWindow
*ow
= iter
->data
;
1439 if (ow
->sink
== NULL
&& ow
->media
== media
&&
1440 purple_strequal(participant
, ow
->participant
) &&
1441 purple_strequal(session_id
, ow
->session_id
)) {
1443 GstElement
*queue
, *convert
, *scale
;
1444 GstElement
*tee
= purple_media_get_tee(media
,
1445 session_id
, participant
);
1450 queue
= gst_element_factory_make("queue", NULL
);
1451 convert
= gst_element_factory_make("videoconvert", NULL
);
1452 scale
= gst_element_factory_make("videoscale", NULL
);
1453 ow
->sink
= purple_media_manager_get_element(
1454 manager
, PURPLE_MEDIA_RECV_VIDEO
,
1455 ow
->media
, ow
->session_id
,
1458 if (participant
== NULL
) {
1459 /* aka this is a preview sink */
1460 GObjectClass
*klass
=
1461 G_OBJECT_GET_CLASS(ow
->sink
);
1462 if (g_object_class_find_property(klass
,
1464 g_object_set(G_OBJECT(ow
->sink
),
1465 "sync", FALSE
, NULL
);
1466 if (g_object_class_find_property(klass
,
1468 g_object_set(G_OBJECT(ow
->sink
),
1469 "async", FALSE
, NULL
);
1472 gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee
)),
1473 queue
, convert
, scale
, ow
->sink
, NULL
);
1475 bus
= gst_pipeline_get_bus(GST_PIPELINE(
1476 manager
->priv
->pipeline
));
1477 g_signal_connect(bus
, "sync-message::element",
1478 G_CALLBACK(window_id_cb
), ow
);
1479 gst_object_unref(bus
);
1481 gst_element_set_state(ow
->sink
, GST_STATE_PLAYING
);
1482 gst_element_set_state(scale
, GST_STATE_PLAYING
);
1483 gst_element_set_state(convert
, GST_STATE_PLAYING
);
1484 gst_element_set_state(queue
, GST_STATE_PLAYING
);
1485 gst_element_link(scale
, ow
->sink
);
1486 gst_element_link(convert
, scale
);
1487 gst_element_link(queue
, convert
);
1488 gst_element_link(tee
, queue
);
1498 purple_media_manager_set_output_window(PurpleMediaManager
*manager
,
1499 PurpleMedia
*media
, const gchar
*session_id
,
1500 const gchar
*participant
, gulong window_id
)
1503 PurpleMediaOutputWindow
*output_window
;
1505 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1506 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1508 output_window
= g_new0(PurpleMediaOutputWindow
, 1);
1509 output_window
->id
= manager
->priv
->next_output_window_id
++;
1510 output_window
->media
= media
;
1511 output_window
->session_id
= g_strdup(session_id
);
1512 output_window
->participant
= g_strdup(participant
);
1513 output_window
->window_id
= window_id
;
1515 manager
->priv
->output_windows
= g_list_prepend(
1516 manager
->priv
->output_windows
, output_window
);
1518 if (purple_media_get_tee(media
, session_id
, participant
) != NULL
)
1519 purple_media_manager_create_output_window(manager
,
1520 media
, session_id
, participant
);
1522 return output_window
->id
;
1529 purple_media_manager_remove_output_window(PurpleMediaManager
*manager
,
1530 gulong output_window_id
)
1533 PurpleMediaOutputWindow
*output_window
= NULL
;
1536 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
), FALSE
);
1538 iter
= manager
->priv
->output_windows
;
1539 for (; iter
; iter
= g_list_next(iter
)) {
1540 PurpleMediaOutputWindow
*ow
= iter
->data
;
1541 if (ow
->id
== output_window_id
) {
1542 manager
->priv
->output_windows
= g_list_delete_link(
1543 manager
->priv
->output_windows
, iter
);
1549 if (output_window
== NULL
)
1552 if (output_window
->sink
!= NULL
) {
1553 GstElement
*element
= output_window
->sink
;
1554 GstPad
*teepad
= NULL
;
1555 GSList
*to_remove
= NULL
;
1557 /* Find the tee element this output is connected to. */
1561 GstElementFactory
*factory
;
1562 const gchar
*factory_name
;
1564 to_remove
= g_slist_append(to_remove
, element
);
1566 pad
= gst_element_get_static_pad(element
, "sink");
1567 peer
= gst_pad_get_peer(pad
);
1569 /* Output is disconnected from the pipeline. */
1570 gst_object_unref(pad
);
1574 factory
= gst_element_get_factory(GST_PAD_PARENT(peer
));
1575 factory_name
= gst_plugin_feature_get_name(factory
);
1576 if (purple_strequal(factory_name
, "tee")) {
1580 element
= GST_PAD_PARENT(peer
);
1582 gst_object_unref(pad
);
1583 gst_object_unref(peer
);
1587 gst_element_release_request_pad(GST_PAD_PARENT(teepad
),
1592 GstElement
*element
= to_remove
->data
;
1594 gst_element_set_locked_state(element
, TRUE
);
1595 gst_element_set_state(element
, GST_STATE_NULL
);
1596 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(element
)),
1598 to_remove
= g_slist_delete_link(to_remove
, to_remove
);
1602 g_free(output_window
->session_id
);
1603 g_free(output_window
->participant
);
1604 g_free(output_window
);
1613 purple_media_manager_remove_output_windows(PurpleMediaManager
*manager
,
1614 PurpleMedia
*media
, const gchar
*session_id
,
1615 const gchar
*participant
)
1620 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1622 iter
= manager
->priv
->output_windows
;
1625 PurpleMediaOutputWindow
*ow
= iter
->data
;
1626 iter
= g_list_next(iter
);
1628 if (media
== ow
->media
&&
1629 purple_strequal(session_id
, ow
->session_id
) &&
1630 purple_strequal(participant
, ow
->participant
))
1631 purple_media_manager_remove_output_window(
1638 purple_media_manager_set_ui_caps(PurpleMediaManager
*manager
,
1639 PurpleMediaCaps caps
)
1642 PurpleMediaCaps oldcaps
;
1644 g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
));
1646 oldcaps
= manager
->priv
->ui_caps
;
1647 manager
->priv
->ui_caps
= caps
;
1649 if (caps
!= oldcaps
)
1650 g_signal_emit(manager
,
1651 purple_media_manager_signals
[UI_CAPS_CHANGED
],
1657 purple_media_manager_get_ui_caps(PurpleMediaManager
*manager
)
1660 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
),
1661 PURPLE_MEDIA_CAPS_NONE
);
1662 return manager
->priv
->ui_caps
;
1664 return PURPLE_MEDIA_CAPS_NONE
;
1669 purple_media_manager_set_backend_type(PurpleMediaManager
*manager
,
1673 g_return_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
));
1675 manager
->priv
->backend_type
= backend_type
;
1680 purple_media_manager_get_backend_type(PurpleMediaManager
*manager
)
1683 g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager
),
1684 PURPLE_MEDIA_CAPS_NONE
);
1686 return manager
->priv
->backend_type
;
1693 purple_media_manager_set_application_data_callbacks(PurpleMediaManager
*manager
,
1694 PurpleMedia
*media
, const gchar
*session_id
,
1695 const gchar
*participant
, PurpleMediaAppDataCallbacks
*callbacks
,
1696 gpointer user_data
, GDestroyNotify notify
)
1698 #ifdef HAVE_MEDIA_APPLICATION
1699 PurpleMediaAppDataInfo
* info
= ensure_app_data_info_and_lock (manager
,
1700 media
, session_id
, participant
);
1703 info
->notify (info
->user_data
);
1705 if (info
->readable_cb_token
) {
1706 g_source_remove (info
->readable_timer_id
);
1707 info
->readable_cb_token
= 0;
1710 if (info
->writable_cb_token
) {
1711 g_source_remove (info
->writable_timer_id
);
1712 info
->writable_cb_token
= 0;
1716 info
->callbacks
= *callbacks
;
1718 info
->callbacks
.writable
= NULL
;
1719 info
->callbacks
.readable
= NULL
;
1721 info
->user_data
= user_data
;
1722 info
->notify
= notify
;
1724 call_appsrc_writable_locked (info
);
1725 if (info
->num_samples
> 0 || info
->current_sample
!= NULL
)
1726 call_appsink_readable_locked (info
);
1728 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1733 purple_media_manager_send_application_data (
1734 PurpleMediaManager
*manager
, PurpleMedia
*media
, const gchar
*session_id
,
1735 const gchar
*participant
, gpointer buffer
, guint size
, gboolean blocking
)
1737 #ifdef HAVE_MEDIA_APPLICATION
1738 PurpleMediaAppDataInfo
* info
= get_app_data_info_and_lock (manager
,
1739 media
, session_id
, participant
);
1741 if (info
&& info
->appsrc
&& info
->connected
) {
1742 GstBuffer
*gstbuffer
= gst_buffer_new_wrapped (g_memdup (buffer
, size
),
1744 GstAppSrc
*appsrc
= gst_object_ref (info
->appsrc
);
1746 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1747 if (gst_app_src_push_buffer (appsrc
, gstbuffer
) == GST_FLOW_OK
) {
1751 srcpad
= gst_element_get_static_pad (GST_ELEMENT (appsrc
),
1754 gst_pad_peer_query (srcpad
, gst_query_new_drain ());
1755 gst_object_unref (srcpad
);
1758 gst_object_unref (appsrc
);
1761 gst_object_unref (appsrc
);
1765 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1773 purple_media_manager_receive_application_data (
1774 PurpleMediaManager
*manager
, PurpleMedia
*media
, const gchar
*session_id
,
1775 const gchar
*participant
, gpointer buffer
, guint max_size
,
1778 #ifdef HAVE_MEDIA_APPLICATION
1779 PurpleMediaAppDataInfo
* info
= get_app_data_info_and_lock (manager
,
1780 media
, session_id
, participant
);
1781 guint bytes_read
= 0;
1784 /* If we are in a blocking read, we need to loop until max_size data
1785 * is read into the buffer, if we're not, then we need to read as much
1789 if (!info
->current_sample
&& info
->appsink
&& info
->num_samples
> 0) {
1790 info
->current_sample
= gst_app_sink_pull_sample (info
->appsink
);
1791 info
->sample_offset
= 0;
1792 if (info
->current_sample
)
1793 info
->num_samples
--;
1796 if (info
->current_sample
) {
1797 GstBuffer
*gstbuffer
= gst_sample_get_buffer (
1798 info
->current_sample
);
1802 guint bytes_to_copy
;
1804 gst_buffer_map (gstbuffer
, &mapinfo
, GST_MAP_READ
);
1805 /* We must copy only the data remaining in the buffer without
1806 * overflowing the buffer */
1807 bytes_to_copy
= max_size
- bytes_read
;
1808 if (bytes_to_copy
> mapinfo
.size
- info
->sample_offset
)
1809 bytes_to_copy
= mapinfo
.size
- info
->sample_offset
;
1810 memcpy ((guint8
*)buffer
+ bytes_read
,
1811 mapinfo
.data
+ info
->sample_offset
, bytes_to_copy
);
1813 gst_buffer_unmap (gstbuffer
, &mapinfo
);
1814 info
->sample_offset
+= bytes_to_copy
;
1815 bytes_read
+= bytes_to_copy
;
1816 if (info
->sample_offset
== mapinfo
.size
) {
1817 gst_sample_unref (info
->current_sample
);
1818 info
->current_sample
= NULL
;
1819 info
->sample_offset
= 0;
1822 /* In case there's no buffer in the sample (should never
1823 * happen), we need to at least unref it */
1824 gst_sample_unref (info
->current_sample
);
1825 info
->current_sample
= NULL
;
1826 info
->sample_offset
= 0;
1830 /* If blocking, wait until there's an available sample */
1831 while (bytes_read
< max_size
&& blocking
&&
1832 info
->current_sample
== NULL
&& info
->num_samples
== 0) {
1833 g_cond_wait (&info
->readable_cond
, &manager
->priv
->appdata_mutex
);
1835 /* We've been signaled, we need to unlock and regrab the info
1836 * struct to make sure nothing changed */
1837 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1838 info
= get_app_data_info_and_lock (manager
,
1839 media
, session_id
, participant
);
1840 if (info
== NULL
|| info
->appsink
== NULL
) {
1841 /* The session was destroyed while we were waiting, we
1842 * should return here */
1843 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1847 } while (bytes_read
< max_size
&&
1848 (blocking
|| info
->num_samples
> 0));
1850 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1853 g_mutex_unlock (&manager
->priv
->appdata_mutex
);
1863 videosink_disable_last_sample(GstElement
*sink
)
1865 GObjectClass
*klass
= G_OBJECT_GET_CLASS(sink
);
1867 if (g_object_class_find_property(klass
, "enable-last-sample")) {
1868 g_object_set(sink
, "enable-last-sample", FALSE
, NULL
);
1872 #if GST_CHECK_VERSION(1, 4, 0)
1874 static PurpleMediaElementType
1875 gst_class_to_purple_element_type(const gchar
*device_class
)
1877 if (purple_strequal(device_class
, "Audio/Source")) {
1878 return PURPLE_MEDIA_ELEMENT_AUDIO
1879 | PURPLE_MEDIA_ELEMENT_SRC
1880 | PURPLE_MEDIA_ELEMENT_ONE_SRC
1881 | PURPLE_MEDIA_ELEMENT_UNIQUE
;
1882 } else if (purple_strequal(device_class
, "Audio/Sink")) {
1883 return PURPLE_MEDIA_ELEMENT_AUDIO
1884 | PURPLE_MEDIA_ELEMENT_SINK
1885 | PURPLE_MEDIA_ELEMENT_ONE_SINK
;
1886 } else if (purple_strequal(device_class
, "Video/Source")) {
1887 return PURPLE_MEDIA_ELEMENT_VIDEO
1888 | PURPLE_MEDIA_ELEMENT_SRC
1889 | PURPLE_MEDIA_ELEMENT_ONE_SRC
1890 | PURPLE_MEDIA_ELEMENT_UNIQUE
;
1891 } else if (purple_strequal(device_class
, "Video/Sink")) {
1892 return PURPLE_MEDIA_ELEMENT_VIDEO
1893 | PURPLE_MEDIA_ELEMENT_SINK
1894 | PURPLE_MEDIA_ELEMENT_ONE_SINK
;
1897 return PURPLE_MEDIA_ELEMENT_NONE
;
1901 gst_device_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
1902 const gchar
*session_id
, const gchar
*participant
)
1906 PurpleMediaElementType type
;
1908 device
= g_object_get_data(G_OBJECT(info
), "gst-device");
1913 result
= gst_device_create_element(device
, NULL
);
1918 type
= purple_media_element_info_get_element_type(info
);
1920 if ((type
& PURPLE_MEDIA_ELEMENT_VIDEO
) &&
1921 (type
& PURPLE_MEDIA_ELEMENT_SINK
)) {
1922 videosink_disable_last_sample(result
);
1929 device_is_ignored(GstDevice
*device
)
1931 gboolean result
= FALSE
;
1933 #if GST_CHECK_VERSION(1, 6, 0)
1934 gchar
*device_class
;
1936 g_return_val_if_fail(device
, TRUE
);
1938 device_class
= gst_device_get_device_class(device
);
1940 /* Ignore PulseAudio monitor audio sources since they have little use
1941 * in the context of telephony.*/
1942 if (purple_strequal(device_class
, "Audio/Source")) {
1943 GstStructure
*properties
;
1944 const gchar
*pa_class
;
1946 properties
= gst_device_get_properties(device
);
1948 pa_class
= gst_structure_get_string(properties
, "device.class");
1949 if (purple_strequal(pa_class
, "monitor")) {
1953 gst_structure_free(properties
);
1956 g_free(device_class
);
1957 #endif /* GST_CHECK_VERSION(1, 6, 0) */
1963 purple_media_manager_register_gst_device(PurpleMediaManager
*manager
,
1966 PurpleMediaElementInfo
*info
;
1967 PurpleMediaElementType type
;
1969 gchar
*device_class
;
1972 if (device_is_ignored(device
)) {
1976 name
= gst_device_get_display_name(device
);
1977 device_class
= gst_device_get_device_class(device
);
1979 id
= g_strdup_printf("%s %s", device_class
, name
);
1981 type
= gst_class_to_purple_element_type(device_class
);
1983 info
= g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
1987 "create-cb", gst_device_create_cb
,
1990 g_object_set_data(G_OBJECT(info
), "gst-device", device
);
1992 purple_media_manager_register_element(manager
, info
);
1994 purple_debug_info("mediamanager", "Registered %s device %s",
1995 device_class
, name
);
1998 g_free(device_class
);
2003 purple_media_manager_unregister_gst_device(PurpleMediaManager
*manager
,
2008 gchar
*device_class
;
2009 gboolean done
= FALSE
;
2011 name
= gst_device_get_display_name(device
);
2012 device_class
= gst_device_get_device_class(device
);
2014 for (i
= manager
->priv
->elements
; i
&& !done
; i
= i
->next
) {
2015 PurpleMediaElementInfo
*info
= i
->data
;
2018 device2
= g_object_get_data(G_OBJECT(info
), "gst-device");
2021 gchar
*device_class2
;
2023 name2
= gst_device_get_display_name(device2
);
2024 device_class2
= gst_device_get_device_class(device2
);
2026 if (purple_strequal(name
, name2
) &&
2027 purple_strequal(device_class
, device_class2
)) {
2030 id
= purple_media_element_info_get_id(info
);
2031 purple_media_manager_unregister_element(manager
,
2034 purple_debug_info("mediamanager",
2035 "Unregistered %s device %s",
2036 device_class
, name
);
2044 g_free(device_class2
);
2049 g_free(device_class
);
2053 device_monitor_bus_cb(GstBus
*bus
, GstMessage
*message
, gpointer user_data
)
2055 PurpleMediaManager
*manager
= user_data
;
2056 GstMessageType message_type
;
2059 message_type
= GST_MESSAGE_TYPE(message
);
2061 if (message_type
== GST_MESSAGE_DEVICE_ADDED
) {
2062 gst_message_parse_device_added(message
, &device
);
2063 purple_media_manager_register_gst_device(manager
, device
);
2064 } else if (message_type
== GST_MESSAGE_DEVICE_REMOVED
) {
2065 gst_message_parse_device_removed (message
, &device
);
2066 purple_media_manager_unregister_gst_device(manager
, device
);
2069 return G_SOURCE_CONTINUE
;
2072 #endif /* GST_CHECK_VERSION(1, 4, 0) */
2075 purple_media_manager_init_device_monitor(PurpleMediaManager
*manager
)
2077 #if GST_CHECK_VERSION(1, 4, 0)
2081 manager
->priv
->device_monitor
= gst_device_monitor_new();
2083 bus
= gst_device_monitor_get_bus(manager
->priv
->device_monitor
);
2084 gst_bus_add_watch (bus
, device_monitor_bus_cb
, manager
);
2085 gst_object_unref (bus
);
2087 /* This avoids warning in GStreamer logs about no filters set */
2088 gst_device_monitor_add_filter(manager
->priv
->device_monitor
, NULL
, NULL
);
2090 gst_device_monitor_start(manager
->priv
->device_monitor
);
2092 i
= gst_device_monitor_get_devices(manager
->priv
->device_monitor
);
2093 for (; i
; i
= g_list_delete_link(i
, i
)) {
2094 GstDevice
*device
= i
->data
;
2096 purple_media_manager_register_gst_device(manager
, device
);
2097 gst_object_unref(device
);
2099 #endif /* GST_CHECK_VERSION(1, 4, 0) */
2103 purple_media_manager_enumerate_elements(PurpleMediaManager
*manager
,
2104 PurpleMediaElementType type
)
2106 GList
*result
= NULL
;
2109 for (i
= manager
->priv
->elements
; i
; i
= i
->next
) {
2110 PurpleMediaElementInfo
*info
= i
->data
;
2111 PurpleMediaElementType type2
;
2113 type2
= purple_media_element_info_get_element_type(info
);
2115 if ((type2
& type
) == type
) {
2117 result
= g_list_prepend(result
, info
);
2125 gst_factory_make_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2126 const gchar
*session_id
, const gchar
*participant
)
2129 GstElement
*element
;
2131 id
= purple_media_element_info_get_id(info
);
2133 element
= gst_element_factory_make(id
, NULL
);
2141 autovideosink_child_added_cb (GstChildProxy
*child_proxy
, GObject
*object
,
2142 gchar
*name
, gpointer user_data
)
2144 videosink_disable_last_sample(GST_ELEMENT(object
));
2148 default_video_sink_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2149 const gchar
*session_id
, const gchar
*participant
)
2151 GstElement
*videosink
= gst_element_factory_make("autovideosink", NULL
);
2153 g_signal_connect(videosink
, "child-added",
2154 G_CALLBACK(autovideosink_child_added_cb
), NULL
);
2160 disabled_video_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2161 const gchar
*session_id
, const gchar
*participant
)
2163 GstElement
*src
= gst_element_factory_make("videotestsrc", NULL
);
2165 /* GST_VIDEO_TEST_SRC_BLACK */
2166 g_object_set(src
, "pattern", 2, NULL
);
2172 test_video_create_cb(PurpleMediaElementInfo
*info
, PurpleMedia
*media
,
2173 const gchar
*session_id
, const gchar
*participant
)
2175 GstElement
*src
= gst_element_factory_make("videotestsrc", NULL
);
2177 g_object_set(src
, "is-live", TRUE
, NULL
);
2183 purple_media_manager_register_static_elements(PurpleMediaManager
*manager
)
2185 static const gchar
*VIDEO_SINK_PLUGINS
[] = {
2186 /* "aasink", "AALib", Didn't work for me */
2187 "directdrawsink", "DirectDraw",
2188 "glimagesink", "OpenGL",
2189 "ximagesink", "X Window System",
2190 "xvimagesink", "X Window System (Xv)",
2193 const gchar
**sinks
= VIDEO_SINK_PLUGINS
;
2195 /* Default auto* elements. */
2197 purple_media_manager_register_element(manager
,
2198 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2199 "id", "autoaudiosrc",
2200 "name", N_("Default"),
2201 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2202 | PURPLE_MEDIA_ELEMENT_SRC
2203 | PURPLE_MEDIA_ELEMENT_ONE_SRC
2204 | PURPLE_MEDIA_ELEMENT_UNIQUE
,
2205 "create-cb", gst_factory_make_cb
,
2208 purple_media_manager_register_element(manager
,
2209 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2210 "id", "autoaudiosink",
2211 "name", N_("Default"),
2212 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2213 | PURPLE_MEDIA_ELEMENT_SINK
2214 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2215 "create-cb", gst_factory_make_cb
,
2218 purple_media_manager_register_element(manager
,
2219 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2220 "id", "autovideosrc",
2221 "name", N_("Default"),
2222 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2223 | PURPLE_MEDIA_ELEMENT_SRC
2224 | PURPLE_MEDIA_ELEMENT_ONE_SRC
2225 | PURPLE_MEDIA_ELEMENT_UNIQUE
,
2226 "create-cb", gst_factory_make_cb
,
2229 purple_media_manager_register_element(manager
,
2230 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2231 "id", "autovideosink",
2232 "name", N_("Default"),
2233 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2234 | PURPLE_MEDIA_ELEMENT_SINK
2235 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2236 "create-cb", default_video_sink_create_cb
,
2239 /* Special elements */
2241 purple_media_manager_register_element(manager
,
2242 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2243 "id", "audiotestsrc",
2244 /* Translators: This is a noun that refers to one
2245 * possible audio input device. The device can help the
2246 * user to check if her speakers or headphones have been
2247 * set up correctly for voice calling. */
2248 "name", N_("Test Sound"),
2249 "type", PURPLE_MEDIA_ELEMENT_AUDIO
2250 | PURPLE_MEDIA_ELEMENT_SRC
2251 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2252 "create-cb", gst_factory_make_cb
,
2255 purple_media_manager_register_element(manager
,
2256 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2257 "id", "disabledvideosrc",
2258 "name", N_("Disabled"),
2259 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2260 | PURPLE_MEDIA_ELEMENT_SRC
2261 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2262 "create-cb", disabled_video_create_cb
,
2265 purple_media_manager_register_element(manager
,
2266 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2267 "id", "videotestsrc",
2268 /* Translators: This is a noun that refers to one
2269 * possible video input device. The device produces
2270 * a test "monoscope" image that can help the user check
2271 * the video output has been set up correctly without
2272 * needing a webcam connected to the computer. */
2273 "name", N_("Test Pattern"),
2274 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2275 | PURPLE_MEDIA_ELEMENT_SRC
2276 | PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2277 "create-cb", test_video_create_cb
,
2280 for (sinks
= VIDEO_SINK_PLUGINS
; sinks
[0]; sinks
+= 2) {
2281 GstElementFactory
*factory
;
2283 factory
= gst_element_factory_find(sinks
[0]);
2288 purple_media_manager_register_element(manager
,
2289 g_object_new(PURPLE_TYPE_MEDIA_ELEMENT_INFO
,
2292 "type", PURPLE_MEDIA_ELEMENT_VIDEO
2293 | PURPLE_MEDIA_ELEMENT_SINK
2294 | PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2295 "create-cb", gst_factory_make_cb
,
2298 gst_object_unref(factory
);
2303 * PurpleMediaElementType
2307 purple_media_element_type_get_type()
2309 static GType type
= 0;
2311 static const GFlagsValue values
[] = {
2312 { PURPLE_MEDIA_ELEMENT_NONE
,
2313 "PURPLE_MEDIA_ELEMENT_NONE", "none" },
2314 { PURPLE_MEDIA_ELEMENT_AUDIO
,
2315 "PURPLE_MEDIA_ELEMENT_AUDIO", "audio" },
2316 { PURPLE_MEDIA_ELEMENT_VIDEO
,
2317 "PURPLE_MEDIA_ELEMENT_VIDEO", "video" },
2318 { PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO
,
2319 "PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO",
2321 { PURPLE_MEDIA_ELEMENT_NO_SRCS
,
2322 "PURPLE_MEDIA_ELEMENT_NO_SRCS", "no-srcs" },
2323 { PURPLE_MEDIA_ELEMENT_ONE_SRC
,
2324 "PURPLE_MEDIA_ELEMENT_ONE_SRC", "one-src" },
2325 { PURPLE_MEDIA_ELEMENT_MULTI_SRC
,
2326 "PURPLE_MEDIA_ELEMENT_MULTI_SRC",
2328 { PURPLE_MEDIA_ELEMENT_REQUEST_SRC
,
2329 "PURPLE_MEDIA_ELEMENT_REQUEST_SRC",
2331 { PURPLE_MEDIA_ELEMENT_NO_SINKS
,
2332 "PURPLE_MEDIA_ELEMENT_NO_SINKS", "no-sinks" },
2333 { PURPLE_MEDIA_ELEMENT_ONE_SINK
,
2334 "PURPLE_MEDIA_ELEMENT_ONE_SINK", "one-sink" },
2335 { PURPLE_MEDIA_ELEMENT_MULTI_SINK
,
2336 "PURPLE_MEDIA_ELEMENT_MULTI_SINK",
2338 { PURPLE_MEDIA_ELEMENT_REQUEST_SINK
,
2339 "PURPLE_MEDIA_ELEMENT_REQUEST_SINK",
2341 { PURPLE_MEDIA_ELEMENT_UNIQUE
,
2342 "PURPLE_MEDIA_ELEMENT_UNIQUE", "unique" },
2343 { PURPLE_MEDIA_ELEMENT_SRC
,
2344 "PURPLE_MEDIA_ELEMENT_SRC", "src" },
2345 { PURPLE_MEDIA_ELEMENT_SINK
,
2346 "PURPLE_MEDIA_ELEMENT_SINK", "sink" },
2347 { PURPLE_MEDIA_ELEMENT_APPLICATION
,
2348 "PURPLE_MEDIA_ELEMENT_APPLICATION", "application" },
2351 type
= g_flags_register_static(
2352 "PurpleMediaElementType", values
);
2359 * PurpleMediaElementInfo
2362 struct _PurpleMediaElementInfoClass
2364 GObjectClass parent_class
;
2367 struct _PurpleMediaElementInfo
2373 struct _PurpleMediaElementInfoPrivate
2377 PurpleMediaElementType type
;
2378 PurpleMediaElementCreateCallback create
;
2389 G_DEFINE_TYPE_WITH_PRIVATE(PurpleMediaElementInfo
,
2390 purple_media_element_info
, G_TYPE_OBJECT
);
2393 purple_media_element_info_init(PurpleMediaElementInfo
*info
)
2395 PurpleMediaElementInfoPrivate
*priv
=
2396 purple_media_element_info_get_instance_private(info
);
2399 priv
->type
= PURPLE_MEDIA_ELEMENT_NONE
;
2400 priv
->create
= NULL
;
2404 purple_media_element_info_finalize(GObject
*info
)
2406 PurpleMediaElementInfoPrivate
*priv
=
2407 purple_media_element_info_get_instance_private(
2408 PURPLE_MEDIA_ELEMENT_INFO(info
));
2412 G_OBJECT_CLASS(purple_media_element_info_parent_class
)->finalize(info
);
2416 purple_media_element_info_set_property (GObject
*object
, guint prop_id
,
2417 const GValue
*value
, GParamSpec
*pspec
)
2419 PurpleMediaElementInfoPrivate
*priv
;
2420 g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object
));
2422 priv
= purple_media_element_info_get_instance_private(
2423 PURPLE_MEDIA_ELEMENT_INFO(object
));
2428 priv
->id
= g_value_dup_string(value
);
2432 priv
->name
= g_value_dup_string(value
);
2435 priv
->type
= g_value_get_flags(value
);
2438 case PROP_CREATE_CB
:
2439 priv
->create
= g_value_get_pointer(value
);
2442 G_OBJECT_WARN_INVALID_PROPERTY_ID(
2443 object
, prop_id
, pspec
);
2449 purple_media_element_info_get_property (GObject
*object
, guint prop_id
,
2450 GValue
*value
, GParamSpec
*pspec
)
2452 PurpleMediaElementInfoPrivate
*priv
;
2453 g_return_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(object
));
2455 priv
= purple_media_element_info_get_instance_private(
2456 PURPLE_MEDIA_ELEMENT_INFO(object
));
2460 g_value_set_string(value
, priv
->id
);
2463 g_value_set_string(value
, priv
->name
);
2466 g_value_set_flags(value
, priv
->type
);
2468 case PROP_CREATE_CB
:
2469 g_value_set_pointer(value
, priv
->create
);
2472 G_OBJECT_WARN_INVALID_PROPERTY_ID(
2473 object
, prop_id
, pspec
);
2479 purple_media_element_info_class_init(PurpleMediaElementInfoClass
*klass
)
2481 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
2483 gobject_class
->finalize
= purple_media_element_info_finalize
;
2484 gobject_class
->set_property
= purple_media_element_info_set_property
;
2485 gobject_class
->get_property
= purple_media_element_info_get_property
;
2487 g_object_class_install_property(gobject_class
, PROP_ID
,
2488 g_param_spec_string("id",
2490 "The unique identifier of the element.",
2492 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2493 G_PARAM_STATIC_STRINGS
));
2495 g_object_class_install_property(gobject_class
, PROP_NAME
,
2496 g_param_spec_string("name",
2498 "The friendly/display name of this element.",
2500 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2501 G_PARAM_STATIC_STRINGS
));
2503 g_object_class_install_property(gobject_class
, PROP_TYPE
,
2504 g_param_spec_flags("type",
2506 "The type of element this is.",
2507 PURPLE_TYPE_MEDIA_ELEMENT_TYPE
,
2508 PURPLE_MEDIA_ELEMENT_NONE
,
2509 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2510 G_PARAM_STATIC_STRINGS
));
2512 g_object_class_install_property(gobject_class
, PROP_CREATE_CB
,
2513 g_param_spec_pointer("create-cb",
2515 "The function called to create this element.",
2516 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
2517 G_PARAM_STATIC_STRINGS
));
2521 purple_media_element_info_get_id(PurpleMediaElementInfo
*info
)
2525 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2526 g_object_get(info
, "id", &id
, NULL
);
2531 purple_media_element_info_get_name(PurpleMediaElementInfo
*info
)
2534 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2535 g_object_get(info
, "name", &name
, NULL
);
2539 PurpleMediaElementType
2540 purple_media_element_info_get_element_type(PurpleMediaElementInfo
*info
)
2542 PurpleMediaElementType type
;
2543 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
),
2544 PURPLE_MEDIA_ELEMENT_NONE
);
2545 g_object_get(info
, "type", &type
, NULL
);
2550 purple_media_element_info_call_create(PurpleMediaElementInfo
*info
,
2551 PurpleMedia
*media
, const gchar
*session_id
,
2552 const gchar
*participant
)
2554 PurpleMediaElementCreateCallback create
;
2555 g_return_val_if_fail(PURPLE_IS_MEDIA_ELEMENT_INFO(info
), NULL
);
2556 g_object_get(info
, "create-cb", &create
, NULL
);
2558 return create(info
, media
, session_id
, participant
);