2 * @file media.c Media API
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
30 #include "media/backend-iface.h"
31 #include "mediamanager.h"
36 #include "media/backend-fs2.h"
37 #include "marshallers.h"
38 #include "media-gst.h"
43 /** @copydoc _PurpleMediaSession */
44 typedef struct _PurpleMediaSession PurpleMediaSession
;
45 /** @copydoc _PurpleMediaStream */
46 typedef struct _PurpleMediaStream PurpleMediaStream
;
47 /** @copydoc _PurpleMediaClass */
48 typedef struct _PurpleMediaClass PurpleMediaClass
;
49 /** @copydoc _PurpleMediaPrivate */
50 typedef struct _PurpleMediaPrivate PurpleMediaPrivate
;
52 /** The media class */
53 struct _PurpleMediaClass
55 GObjectClass parent_class
; /**< The parent class. */
58 /** The media class's private data */
61 GObject parent
; /**< The parent of this object. */
62 PurpleMediaPrivate
*priv
; /**< The private data of this object. */
65 struct _PurpleMediaSession
69 PurpleMediaSessionType type
;
73 struct _PurpleMediaStream
75 PurpleMediaSession
*session
;
78 GList
*local_candidates
;
79 GList
*remote_candidates
;
83 gboolean candidates_prepared
;
85 GList
*active_local_candidates
;
86 GList
*active_remote_candidates
;
90 struct _PurpleMediaPrivate
93 PurpleMediaManager
*manager
;
94 PurpleAccount
*account
;
95 PurpleMediaBackend
*backend
;
96 gchar
*conference_type
;
100 GHashTable
*sessions
; /* PurpleMediaSession table */
102 GList
*streams
; /* PurpleMediaStream table */
109 #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate))
111 static void purple_media_class_init (PurpleMediaClass
*klass
);
112 static void purple_media_init (PurpleMedia
*media
);
113 static void purple_media_dispose (GObject
*object
);
114 static void purple_media_finalize (GObject
*object
);
115 static void purple_media_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
);
116 static void purple_media_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
);
118 static void purple_media_new_local_candidate_cb(PurpleMediaBackend
*backend
,
119 const gchar
*sess_id
, const gchar
*participant
,
120 PurpleMediaCandidate
*candidate
, PurpleMedia
*media
);
121 static void purple_media_candidates_prepared_cb(PurpleMediaBackend
*backend
,
122 const gchar
*sess_id
, const gchar
*name
, PurpleMedia
*media
);
123 static void purple_media_candidate_pair_established_cb(
124 PurpleMediaBackend
*backend
,
125 const gchar
*sess_id
, const gchar
*name
,
126 PurpleMediaCandidate
*local_candidate
,
127 PurpleMediaCandidate
*remote_candidate
,
129 static void purple_media_codecs_changed_cb(PurpleMediaBackend
*backend
,
130 const gchar
*sess_id
, PurpleMedia
*media
);
132 static GObjectClass
*parent_class
= NULL
;
146 static guint purple_media_signals
[LAST_SIGNAL
] = {0};
153 PROP_CONFERENCE_TYPE
,
161 purple_media_get_type()
164 static GType type
= 0;
167 static const GTypeInfo info
= {
168 sizeof(PurpleMediaClass
),
171 (GClassInitFunc
) purple_media_class_init
,
176 (GInstanceInitFunc
) purple_media_init
,
179 type
= g_type_register_static(G_TYPE_OBJECT
, "PurpleMedia", &info
, 0);
189 purple_media_class_init (PurpleMediaClass
*klass
)
191 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
192 parent_class
= g_type_class_peek_parent(klass
);
194 gobject_class
->dispose
= purple_media_dispose
;
195 gobject_class
->finalize
= purple_media_finalize
;
196 gobject_class
->set_property
= purple_media_set_property
;
197 gobject_class
->get_property
= purple_media_get_property
;
199 g_object_class_install_property(gobject_class
, PROP_MANAGER
,
200 g_param_spec_object("manager",
201 "Purple Media Manager",
202 "The media manager that contains this media session.",
203 PURPLE_TYPE_MEDIA_MANAGER
,
204 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
207 * This one should be PURPLE_TYPE_MEDIA_BACKEND, but it doesn't
208 * like interfaces because they "aren't GObjects"
210 g_object_class_install_property(gobject_class
, PROP_BACKEND
,
211 g_param_spec_object("backend",
212 "Purple Media Backend",
213 "The backend object this media object uses.",
217 g_object_class_install_property(gobject_class
, PROP_ACCOUNT
,
218 g_param_spec_pointer("account",
220 "The account this media session is on.",
221 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
223 g_object_class_install_property(gobject_class
, PROP_CONFERENCE_TYPE
,
224 g_param_spec_string("conference-type",
226 "The type of conference that this media object "
227 "has been created to provide.",
229 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
231 g_object_class_install_property(gobject_class
, PROP_INITIATOR
,
232 g_param_spec_boolean("initiator",
234 "If the local user initiated the conference.",
236 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
));
238 g_object_class_install_property(gobject_class
, PROP_PRPL_DATA
,
239 g_param_spec_pointer("prpl-data",
241 "Data the prpl plugin set on the media session.",
244 purple_media_signals
[S_ERROR
] = g_signal_new("error", G_TYPE_FROM_CLASS(klass
),
245 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
246 g_cclosure_marshal_VOID__STRING
,
247 G_TYPE_NONE
, 1, G_TYPE_STRING
);
248 purple_media_signals
[CANDIDATES_PREPARED
] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass
),
249 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
250 purple_smarshal_VOID__STRING_STRING
,
251 G_TYPE_NONE
, 2, G_TYPE_STRING
,
253 purple_media_signals
[CODECS_CHANGED
] = g_signal_new("codecs-changed", G_TYPE_FROM_CLASS(klass
),
254 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
255 g_cclosure_marshal_VOID__STRING
,
256 G_TYPE_NONE
, 1, G_TYPE_STRING
);
257 purple_media_signals
[LEVEL
] = g_signal_new("level", G_TYPE_FROM_CLASS(klass
),
258 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
259 purple_smarshal_VOID__STRING_STRING_DOUBLE
,
260 G_TYPE_NONE
, 3, G_TYPE_STRING
,
261 G_TYPE_STRING
, G_TYPE_DOUBLE
);
262 purple_media_signals
[NEW_CANDIDATE
] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass
),
263 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
264 purple_smarshal_VOID__POINTER_POINTER_OBJECT
,
265 G_TYPE_NONE
, 3, G_TYPE_POINTER
,
266 G_TYPE_POINTER
, PURPLE_TYPE_MEDIA_CANDIDATE
);
267 purple_media_signals
[STATE_CHANGED
] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass
),
268 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
269 purple_smarshal_VOID__ENUM_STRING_STRING
,
270 G_TYPE_NONE
, 3, PURPLE_MEDIA_TYPE_STATE
,
271 G_TYPE_STRING
, G_TYPE_STRING
);
272 purple_media_signals
[STREAM_INFO
] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass
),
273 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
274 purple_smarshal_VOID__ENUM_STRING_STRING_BOOLEAN
,
275 G_TYPE_NONE
, 4, PURPLE_MEDIA_TYPE_INFO_TYPE
,
276 G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
277 g_type_class_add_private(klass
, sizeof(PurpleMediaPrivate
));
282 purple_media_init (PurpleMedia
*media
)
284 media
->priv
= PURPLE_MEDIA_GET_PRIVATE(media
);
285 memset(media
->priv
, 0, sizeof(*media
->priv
));
289 purple_media_stream_free(PurpleMediaStream
*stream
)
294 g_free(stream
->participant
);
296 if (stream
->local_candidates
)
297 purple_media_candidate_list_free(stream
->local_candidates
);
298 if (stream
->remote_candidates
)
299 purple_media_candidate_list_free(stream
->remote_candidates
);
301 if (stream
->active_local_candidates
)
302 purple_media_candidate_list_free(
303 stream
->active_local_candidates
);
304 if (stream
->active_remote_candidates
)
305 purple_media_candidate_list_free(
306 stream
->active_remote_candidates
);
312 purple_media_session_free(PurpleMediaSession
*session
)
322 purple_media_dispose(GObject
*media
)
324 PurpleMediaPrivate
*priv
= PURPLE_MEDIA_GET_PRIVATE(media
);
326 purple_debug_info("media","purple_media_dispose\n");
328 purple_media_manager_remove_media(priv
->manager
, PURPLE_MEDIA(media
));
331 g_object_unref(priv
->backend
);
332 priv
->backend
= NULL
;
336 g_object_unref(priv
->manager
);
337 priv
->manager
= NULL
;
340 G_OBJECT_CLASS(parent_class
)->dispose(media
);
344 purple_media_finalize(GObject
*media
)
346 PurpleMediaPrivate
*priv
= PURPLE_MEDIA_GET_PRIVATE(media
);
347 purple_debug_info("media","purple_media_finalize\n");
349 for (; priv
->streams
; priv
->streams
= g_list_delete_link(priv
->streams
, priv
->streams
))
350 purple_media_stream_free(priv
->streams
->data
);
352 for (; priv
->participants
; priv
->participants
= g_list_delete_link(
353 priv
->participants
, priv
->participants
))
354 g_free(priv
->participants
->data
);
356 if (priv
->sessions
) {
357 GList
*sessions
= g_hash_table_get_values(priv
->sessions
);
358 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
359 purple_media_session_free(sessions
->data
);
361 g_hash_table_destroy(priv
->sessions
);
364 G_OBJECT_CLASS(parent_class
)->finalize(media
);
368 purple_media_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
371 g_return_if_fail(PURPLE_IS_MEDIA(object
));
373 media
= PURPLE_MEDIA(object
);
377 media
->priv
->manager
= g_value_dup_object(value
);
380 media
->priv
->account
= g_value_get_pointer(value
);
382 case PROP_CONFERENCE_TYPE
:
383 media
->priv
->conference_type
=
384 g_value_dup_string(value
);
385 media
->priv
->backend
= g_object_new(
386 purple_media_manager_get_backend_type(
387 purple_media_manager_get()),
389 media
->priv
->conference_type
,
392 g_signal_connect(media
->priv
->backend
,
393 "active-candidate-pair",
395 purple_media_candidate_pair_established_cb
),
397 g_signal_connect(media
->priv
->backend
,
398 "candidates-prepared",
400 purple_media_candidates_prepared_cb
),
402 g_signal_connect(media
->priv
->backend
,
405 purple_media_codecs_changed_cb
),
407 g_signal_connect(media
->priv
->backend
,
410 purple_media_new_local_candidate_cb
),
414 media
->priv
->initiator
= g_value_get_boolean(value
);
417 media
->priv
->prpl_data
= g_value_get_pointer(value
);
420 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
426 purple_media_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
429 g_return_if_fail(PURPLE_IS_MEDIA(object
));
431 media
= PURPLE_MEDIA(object
);
435 g_value_set_object(value
, media
->priv
->manager
);
438 g_value_set_object(value
, media
->priv
->backend
);
441 g_value_set_pointer(value
, media
->priv
->account
);
443 case PROP_CONFERENCE_TYPE
:
444 g_value_set_string(value
,
445 media
->priv
->conference_type
);
448 g_value_set_boolean(value
, media
->priv
->initiator
);
451 g_value_set_pointer(value
, media
->priv
->prpl_data
);
454 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
460 static PurpleMediaSession
*
461 purple_media_get_session(PurpleMedia
*media
, const gchar
*sess_id
)
463 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
464 return (PurpleMediaSession
*) (media
->priv
->sessions
) ?
465 g_hash_table_lookup(media
->priv
->sessions
, sess_id
) : NULL
;
468 static PurpleMediaStream
*
469 purple_media_get_stream(PurpleMedia
*media
, const gchar
*session
, const gchar
*participant
)
473 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
475 streams
= media
->priv
->streams
;
477 for (; streams
; streams
= g_list_next(streams
)) {
478 PurpleMediaStream
*stream
= streams
->data
;
479 if (!strcmp(stream
->session
->id
, session
) &&
480 !strcmp(stream
->participant
, participant
))
488 purple_media_get_streams(PurpleMedia
*media
, const gchar
*session
,
489 const gchar
*participant
)
494 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
496 streams
= media
->priv
->streams
;
498 for (; streams
; streams
= g_list_next(streams
)) {
499 PurpleMediaStream
*stream
= streams
->data
;
500 if ((session
== NULL
||
501 !strcmp(stream
->session
->id
, session
)) &&
502 (participant
== NULL
||
503 !strcmp(stream
->participant
, participant
)))
504 ret
= g_list_append(ret
, stream
);
511 purple_media_add_session(PurpleMedia
*media
, PurpleMediaSession
*session
)
513 g_return_if_fail(PURPLE_IS_MEDIA(media
));
514 g_return_if_fail(session
!= NULL
);
516 if (!media
->priv
->sessions
) {
517 purple_debug_info("media", "Creating hash table for sessions\n");
518 media
->priv
->sessions
= g_hash_table_new(g_str_hash
, g_str_equal
);
520 g_hash_table_insert(media
->priv
->sessions
, g_strdup(session
->id
), session
);
525 purple_media_remove_session(PurpleMedia
*media
, PurpleMediaSession
*session
)
527 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
528 return g_hash_table_remove(media
->priv
->sessions
, session
->id
);
532 static PurpleMediaStream
*
533 purple_media_insert_stream(PurpleMediaSession
*session
,
534 const gchar
*name
, gboolean initiator
)
536 PurpleMediaStream
*media_stream
;
538 g_return_val_if_fail(session
!= NULL
, NULL
);
540 media_stream
= g_new0(PurpleMediaStream
, 1);
541 media_stream
->participant
= g_strdup(name
);
542 media_stream
->session
= session
;
543 media_stream
->initiator
= initiator
;
545 session
->media
->priv
->streams
=
546 g_list_append(session
->media
->priv
->streams
, media_stream
);
552 purple_media_insert_local_candidate(PurpleMediaSession
*session
, const gchar
*name
,
553 PurpleMediaCandidate
*candidate
)
555 PurpleMediaStream
*stream
;
557 g_return_if_fail(session
!= NULL
);
559 stream
= purple_media_get_stream(session
->media
, session
->id
, name
);
560 stream
->local_candidates
= g_list_append(stream
->local_candidates
, candidate
);
565 purple_media_get_session_ids(PurpleMedia
*media
)
568 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
569 return media
->priv
->sessions
!= NULL
?
570 g_hash_table_get_keys(media
->priv
->sessions
) : NULL
;
578 purple_media_get_src(PurpleMedia
*media
, const gchar
*sess_id
)
581 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
583 if (PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
))
584 return purple_media_backend_fs2_get_src(
585 PURPLE_MEDIA_BACKEND_FS2(
586 media
->priv
->backend
), sess_id
);
588 g_return_val_if_reached(NULL
);
593 #endif /* USE_GSTREAMER */
596 purple_media_get_account(PurpleMedia
*media
)
599 PurpleAccount
*account
;
600 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
601 g_object_get(G_OBJECT(media
), "account", &account
, NULL
);
609 purple_media_get_prpl_data(PurpleMedia
*media
)
613 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
614 g_object_get(G_OBJECT(media
), "prpl-data", &prpl_data
, NULL
);
622 purple_media_set_prpl_data(PurpleMedia
*media
, gpointer prpl_data
)
625 g_return_if_fail(PURPLE_IS_MEDIA(media
));
626 g_object_set(G_OBJECT(media
), "prpl-data", prpl_data
, NULL
);
631 purple_media_error(PurpleMedia
*media
, const gchar
*error
, ...)
637 g_return_if_fail(PURPLE_IS_MEDIA(media
));
639 va_start(args
, error
);
640 message
= g_strdup_vprintf(error
, args
);
643 purple_debug_error("media", "%s\n", message
);
644 g_signal_emit(media
, purple_media_signals
[S_ERROR
], 0, message
);
651 purple_media_end(PurpleMedia
*media
,
652 const gchar
*session_id
, const gchar
*participant
)
655 GList
*iter
, *sessions
= NULL
, *participants
= NULL
;
657 g_return_if_fail(PURPLE_IS_MEDIA(media
));
659 iter
= purple_media_get_streams(media
, session_id
, participant
);
661 /* Free matching streams */
662 for (; iter
; iter
= g_list_delete_link(iter
, iter
)) {
663 PurpleMediaStream
*stream
= iter
->data
;
665 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
666 0, PURPLE_MEDIA_STATE_END
,
667 stream
->session
->id
, stream
->participant
);
669 media
->priv
->streams
=
670 g_list_remove(media
->priv
->streams
, stream
);
672 if (g_list_find(sessions
, stream
->session
) == NULL
)
673 sessions
= g_list_prepend(sessions
, stream
->session
);
675 if (g_list_find_custom(participants
, stream
->participant
,
676 (GCompareFunc
)strcmp
) == NULL
)
677 participants
= g_list_prepend(participants
,
678 g_strdup(stream
->participant
));
680 purple_media_stream_free(stream
);
683 iter
= media
->priv
->streams
;
685 /* Reduce to list of sessions to remove */
686 for (; iter
; iter
= g_list_next(iter
)) {
687 PurpleMediaStream
*stream
= iter
->data
;
689 sessions
= g_list_remove(sessions
, stream
->session
);
692 /* Free sessions with no streams left */
693 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
694 PurpleMediaSession
*session
= sessions
->data
;
696 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
697 0, PURPLE_MEDIA_STATE_END
,
700 g_hash_table_remove(media
->priv
->sessions
, session
->id
);
701 purple_media_session_free(session
);
704 iter
= media
->priv
->streams
;
706 /* Reduce to list of participants to remove */
707 for (; iter
; iter
= g_list_next(iter
)) {
708 PurpleMediaStream
*stream
= iter
->data
;
711 tmp
= g_list_find_custom(participants
,
712 stream
->participant
, (GCompareFunc
)strcmp
);
716 participants
= g_list_delete_link(participants
, tmp
);
720 /* Remove participants with no streams left (just emit the signal) */
721 for (; participants
; participants
=
722 g_list_delete_link(participants
, participants
)) {
723 gchar
*participant
= participants
->data
;
724 GList
*link
= g_list_find_custom(media
->priv
->participants
,
725 participant
, (GCompareFunc
)strcmp
);
727 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
728 0, PURPLE_MEDIA_STATE_END
,
733 media
->priv
->participants
= g_list_delete_link(
734 media
->priv
->participants
, link
);
740 /* Free the conference if no sessions left */
741 if (media
->priv
->sessions
!= NULL
&&
742 g_hash_table_size(media
->priv
->sessions
) == 0) {
743 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
744 0, PURPLE_MEDIA_STATE_END
,
746 g_object_unref(media
);
753 purple_media_stream_info(PurpleMedia
*media
, PurpleMediaInfoType type
,
754 const gchar
*session_id
, const gchar
*participant
,
758 g_return_if_fail(PURPLE_IS_MEDIA(media
));
760 if (type
== PURPLE_MEDIA_INFO_ACCEPT
) {
761 GList
*streams
, *sessions
= NULL
, *participants
= NULL
;
763 g_return_if_fail(PURPLE_IS_MEDIA(media
));
765 streams
= purple_media_get_streams(media
,
766 session_id
, participant
);
768 /* Emit stream acceptance */
769 for (; streams
; streams
=
770 g_list_delete_link(streams
, streams
)) {
771 PurpleMediaStream
*stream
= streams
->data
;
773 stream
->accepted
= TRUE
;
776 purple_media_signals
[STREAM_INFO
],
777 0, type
, stream
->session
->id
,
778 stream
->participant
, local
);
780 if (g_list_find(sessions
, stream
->session
) == NULL
)
781 sessions
= g_list_prepend(sessions
,
784 if (g_list_find_custom(participants
,
786 (GCompareFunc
)strcmp
) == NULL
)
787 participants
= g_list_prepend(participants
,
788 g_strdup(stream
->participant
));
791 /* Emit session acceptance */
792 for (; sessions
; sessions
=
793 g_list_delete_link(sessions
, sessions
)) {
794 PurpleMediaSession
*session
= sessions
->data
;
796 if (purple_media_accepted(media
, session
->id
, NULL
))
797 g_signal_emit(media
, purple_media_signals
[
799 PURPLE_MEDIA_INFO_ACCEPT
,
800 session
->id
, NULL
, local
);
803 /* Emit participant acceptance */
804 for (; participants
; participants
= g_list_delete_link(
805 participants
, participants
)) {
806 gchar
*participant
= participants
->data
;
808 if (purple_media_accepted(media
, NULL
, participant
))
809 g_signal_emit(media
, purple_media_signals
[
811 PURPLE_MEDIA_INFO_ACCEPT
,
812 NULL
, participant
, local
);
817 /* Emit conference acceptance */
818 if (purple_media_accepted(media
, NULL
, NULL
))
820 purple_media_signals
[STREAM_INFO
],
821 0, PURPLE_MEDIA_INFO_ACCEPT
,
825 } else if (type
== PURPLE_MEDIA_INFO_HANGUP
||
826 type
== PURPLE_MEDIA_INFO_REJECT
) {
829 g_return_if_fail(PURPLE_IS_MEDIA(media
));
831 streams
= purple_media_get_streams(media
,
832 session_id
, participant
);
834 /* Emit for stream */
835 for (; streams
; streams
=
836 g_list_delete_link(streams
, streams
)) {
837 PurpleMediaStream
*stream
= streams
->data
;
840 purple_media_signals
[STREAM_INFO
],
841 0, type
, stream
->session
->id
,
842 stream
->participant
, local
);
845 if (session_id
!= NULL
&& participant
!= NULL
) {
846 /* Everything that needs to be emitted has been */
847 } else if (session_id
== NULL
&& participant
== NULL
) {
848 /* Emit for everything in the conference */
849 GList
*sessions
= NULL
;
850 GList
*participants
= media
->priv
->participants
;
852 if (media
->priv
->sessions
!= NULL
)
853 sessions
= g_hash_table_get_values(
854 media
->priv
->sessions
);
856 /* Emit for sessions */
857 for (; sessions
; sessions
= g_list_delete_link(
858 sessions
, sessions
)) {
859 PurpleMediaSession
*session
= sessions
->data
;
861 g_signal_emit(media
, purple_media_signals
[
862 STREAM_INFO
], 0, type
,
863 session
->id
, NULL
, local
);
866 /* Emit for participants */
867 for (; participants
; participants
=
868 g_list_next(participants
)) {
869 gchar
*participant
= participants
->data
;
871 g_signal_emit(media
, purple_media_signals
[
872 STREAM_INFO
], 0, type
,
873 NULL
, participant
, local
);
876 /* Emit for conference */
878 purple_media_signals
[STREAM_INFO
],
879 0, type
, NULL
, NULL
, local
);
880 } else if (session_id
!= NULL
) {
881 /* Emit just the specific session */
882 PurpleMediaSession
*session
=
883 purple_media_get_session(
886 if (session
== NULL
) {
887 purple_debug_warning("media",
888 "Couldn't find session"
889 " to hangup/reject.\n");
891 g_signal_emit(media
, purple_media_signals
[
892 STREAM_INFO
], 0, type
,
893 session
->id
, NULL
, local
);
895 } else if (participant
!= NULL
) {
896 /* Emit just the specific participant */
897 if (!g_list_find_custom(media
->priv
->participants
,
898 participant
, (GCompareFunc
)strcmp
)) {
899 purple_debug_warning("media",
900 "Couldn't find participant"
901 " to hangup/reject.\n");
903 g_signal_emit(media
, purple_media_signals
[
904 STREAM_INFO
], 0, type
, NULL
,
909 purple_media_end(media
, session_id
, participant
);
913 g_signal_emit(media
, purple_media_signals
[STREAM_INFO
],
914 0, type
, session_id
, participant
, local
);
920 purple_media_new_local_candidate_cb(PurpleMediaBackend
*backend
,
921 const gchar
*sess_id
, const gchar
*participant
,
922 PurpleMediaCandidate
*candidate
, PurpleMedia
*media
)
924 PurpleMediaSession
*session
=
925 purple_media_get_session(media
, sess_id
);
927 purple_media_insert_local_candidate(session
, participant
,
928 purple_media_candidate_copy(candidate
));
930 g_signal_emit(session
->media
, purple_media_signals
[NEW_CANDIDATE
],
931 0, session
->id
, participant
, candidate
);
935 purple_media_candidates_prepared_cb(PurpleMediaBackend
*backend
,
936 const gchar
*sess_id
, const gchar
*name
, PurpleMedia
*media
)
938 PurpleMediaStream
*stream_data
;
940 g_return_if_fail(PURPLE_IS_MEDIA(media
));
942 stream_data
= purple_media_get_stream(media
, sess_id
, name
);
943 stream_data
->candidates_prepared
= TRUE
;
945 g_signal_emit(media
, purple_media_signals
[CANDIDATES_PREPARED
],
949 /* callback called when a pair of transport candidates (local and remote)
950 * has been established */
952 purple_media_candidate_pair_established_cb(PurpleMediaBackend
*backend
,
953 const gchar
*sess_id
, const gchar
*name
,
954 PurpleMediaCandidate
*local_candidate
,
955 PurpleMediaCandidate
*remote_candidate
,
958 PurpleMediaStream
*stream
;
962 g_return_if_fail(PURPLE_IS_MEDIA(media
));
964 stream
= purple_media_get_stream(media
, sess_id
, name
);
965 id
= purple_media_candidate_get_component_id(local_candidate
);
967 iter
= stream
->active_local_candidates
;
968 for(; iter
; iter
= g_list_next(iter
)) {
969 PurpleMediaCandidate
*c
= iter
->data
;
970 if (id
== purple_media_candidate_get_component_id(c
)) {
972 stream
->active_local_candidates
=
973 g_list_delete_link(iter
, iter
);
974 stream
->active_local_candidates
= g_list_prepend(
975 stream
->active_local_candidates
,
976 purple_media_candidate_copy(
982 stream
->active_local_candidates
= g_list_prepend(
983 stream
->active_local_candidates
,
984 purple_media_candidate_copy(
987 id
= purple_media_candidate_get_component_id(local_candidate
);
989 iter
= stream
->active_remote_candidates
;
990 for(; iter
; iter
= g_list_next(iter
)) {
991 PurpleMediaCandidate
*c
= iter
->data
;
992 if (id
== purple_media_candidate_get_component_id(c
)) {
994 stream
->active_remote_candidates
=
995 g_list_delete_link(iter
, iter
);
996 stream
->active_remote_candidates
= g_list_prepend(
997 stream
->active_remote_candidates
,
998 purple_media_candidate_copy(
1004 stream
->active_remote_candidates
= g_list_prepend(
1005 stream
->active_remote_candidates
,
1006 purple_media_candidate_copy(
1009 purple_debug_info("media", "candidate pair established\n");
1013 purple_media_codecs_changed_cb(PurpleMediaBackend
*backend
,
1014 const gchar
*sess_id
, PurpleMedia
*media
)
1016 g_signal_emit(media
, purple_media_signals
[CODECS_CHANGED
], 0, sess_id
);
1021 purple_media_add_stream(PurpleMedia
*media
, const gchar
*sess_id
,
1022 const gchar
*who
, PurpleMediaSessionType type
,
1023 gboolean initiator
, const gchar
*transmitter
,
1024 guint num_params
, GParameter
*params
)
1027 PurpleMediaSession
*session
;
1028 PurpleMediaStream
*stream
= NULL
;
1030 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1032 if (!purple_media_backend_add_stream(media
->priv
->backend
,
1033 sess_id
, who
, type
, initiator
, transmitter
,
1034 num_params
, params
)) {
1035 purple_debug_error("media", "Error adding stream.\n");
1039 session
= purple_media_get_session(media
, sess_id
);
1042 session
= g_new0(PurpleMediaSession
, 1);
1043 session
->id
= g_strdup(sess_id
);
1044 session
->media
= media
;
1045 session
->type
= type
;
1046 session
->initiator
= initiator
;
1048 purple_media_add_session(media
, session
);
1049 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
1050 0, PURPLE_MEDIA_STATE_NEW
,
1054 if (!g_list_find_custom(media
->priv
->participants
,
1055 who
, (GCompareFunc
)strcmp
)) {
1056 media
->priv
->participants
= g_list_prepend(
1057 media
->priv
->participants
, g_strdup(who
));
1059 g_signal_emit_by_name(media
, "state-changed",
1060 PURPLE_MEDIA_STATE_NEW
, NULL
, who
);
1063 if (purple_media_get_stream(media
, sess_id
, who
) == NULL
) {
1064 stream
= purple_media_insert_stream(session
, who
, initiator
);
1066 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
1067 0, PURPLE_MEDIA_STATE_NEW
,
1077 PurpleMediaManager
*
1078 purple_media_get_manager(PurpleMedia
*media
)
1080 PurpleMediaManager
*ret
;
1081 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1082 g_object_get(media
, "manager", &ret
, NULL
);
1086 PurpleMediaSessionType
1087 purple_media_get_session_type(PurpleMedia
*media
, const gchar
*sess_id
)
1090 PurpleMediaSession
*session
;
1091 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), PURPLE_MEDIA_NONE
);
1092 session
= purple_media_get_session(media
, sess_id
);
1093 return session
->type
;
1095 return PURPLE_MEDIA_NONE
;
1098 /* XXX: Should wait until codecs-ready is TRUE before using this function */
1100 purple_media_get_codecs(PurpleMedia
*media
, const gchar
*sess_id
)
1103 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1105 return purple_media_backend_get_codecs(media
->priv
->backend
, sess_id
);
1112 purple_media_get_local_candidates(PurpleMedia
*media
, const gchar
*sess_id
,
1113 const gchar
*participant
)
1116 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1118 return purple_media_backend_get_local_candidates(media
->priv
->backend
,
1119 sess_id
, participant
);
1126 purple_media_add_remote_candidates(PurpleMedia
*media
, const gchar
*sess_id
,
1127 const gchar
*participant
,
1128 GList
*remote_candidates
)
1131 PurpleMediaStream
*stream
;
1133 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1134 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1136 if (stream
== NULL
) {
1137 purple_debug_error("media",
1138 "purple_media_add_remote_candidates: "
1139 "couldn't find stream %s %s.\n",
1140 sess_id
? sess_id
: "(null)",
1141 participant
? participant
: "(null)");
1145 stream
->remote_candidates
= g_list_concat(stream
->remote_candidates
,
1146 purple_media_candidate_list_copy(remote_candidates
));
1148 purple_media_backend_add_remote_candidates(media
->priv
->backend
,
1149 sess_id
, participant
, remote_candidates
);
1155 * These two functions aren't being used and I'd rather not lock in the API
1156 * until they are needed. If they ever are.
1160 purple_media_get_active_local_candidates(PurpleMedia
*media
,
1161 const gchar
*sess_id
, const gchar
*participant
)
1164 PurpleMediaStream
*stream
;
1165 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1166 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1167 return purple_media_candidate_list_copy(
1168 stream
->active_local_candidates
);
1175 purple_media_get_active_remote_candidates(PurpleMedia
*media
,
1176 const gchar
*sess_id
, const gchar
*participant
)
1179 PurpleMediaStream
*stream
;
1180 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1181 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1182 return purple_media_candidate_list_copy(
1183 stream
->active_remote_candidates
);
1191 purple_media_set_remote_codecs(PurpleMedia
*media
, const gchar
*sess_id
,
1192 const gchar
*participant
, GList
*codecs
)
1195 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1197 return purple_media_backend_set_remote_codecs(media
->priv
->backend
,
1198 sess_id
, participant
, codecs
);
1205 purple_media_candidates_prepared(PurpleMedia
*media
,
1206 const gchar
*session_id
, const gchar
*participant
)
1210 gboolean prepared
= TRUE
;
1212 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1214 streams
= purple_media_get_streams(media
, session_id
, participant
);
1216 for (; streams
; streams
= g_list_delete_link(streams
, streams
)) {
1217 PurpleMediaStream
*stream
= streams
->data
;
1218 if (stream
->candidates_prepared
== FALSE
) {
1219 g_list_free(streams
);
1232 purple_media_set_send_codec(PurpleMedia
*media
, const gchar
*sess_id
, PurpleMediaCodec
*codec
)
1235 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1237 return purple_media_backend_set_send_codec(
1238 media
->priv
->backend
, sess_id
, codec
);
1245 purple_media_codecs_ready(PurpleMedia
*media
, const gchar
*sess_id
)
1248 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1250 return purple_media_backend_codecs_ready(
1251 media
->priv
->backend
, sess_id
);
1258 purple_media_is_initiator(PurpleMedia
*media
,
1259 const gchar
*sess_id
, const gchar
*participant
)
1262 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1264 if (sess_id
== NULL
&& participant
== NULL
)
1265 return media
->priv
->initiator
;
1266 else if (sess_id
!= NULL
&& participant
== NULL
) {
1267 PurpleMediaSession
*session
=
1268 purple_media_get_session(media
, sess_id
);
1269 return session
!= NULL
? session
->initiator
: FALSE
;
1270 } else if (sess_id
!= NULL
&& participant
!= NULL
) {
1271 PurpleMediaStream
*stream
= purple_media_get_stream(
1272 media
, sess_id
, participant
);
1273 return stream
!= NULL
? stream
->initiator
: FALSE
;
1280 purple_media_accepted(PurpleMedia
*media
, const gchar
*sess_id
,
1281 const gchar
*participant
)
1284 gboolean accepted
= TRUE
;
1286 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1288 if (sess_id
== NULL
&& participant
== NULL
) {
1289 GList
*streams
= media
->priv
->streams
;
1291 for (; streams
; streams
= g_list_next(streams
)) {
1292 PurpleMediaStream
*stream
= streams
->data
;
1293 if (stream
->accepted
== FALSE
) {
1298 } else if (sess_id
!= NULL
&& participant
== NULL
) {
1299 GList
*streams
= purple_media_get_streams(
1300 media
, sess_id
, NULL
);
1301 for (; streams
; streams
=
1302 g_list_delete_link(streams
, streams
)) {
1303 PurpleMediaStream
*stream
= streams
->data
;
1304 if (stream
->accepted
== FALSE
) {
1305 g_list_free(streams
);
1310 } else if (sess_id
!= NULL
&& participant
!= NULL
) {
1311 PurpleMediaStream
*stream
= purple_media_get_stream(
1312 media
, sess_id
, participant
);
1313 if (stream
== NULL
|| stream
->accepted
== FALSE
)
1323 void purple_media_set_input_volume(PurpleMedia
*media
,
1324 const gchar
*session_id
, double level
)
1327 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1328 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
));
1330 purple_media_backend_fs2_set_input_volume(
1331 PURPLE_MEDIA_BACKEND_FS2(
1332 media
->priv
->backend
),
1337 void purple_media_set_output_volume(PurpleMedia
*media
,
1338 const gchar
*session_id
, const gchar
*participant
,
1342 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1343 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
));
1345 purple_media_backend_fs2_set_output_volume(
1346 PURPLE_MEDIA_BACKEND_FS2(
1347 media
->priv
->backend
),
1348 session_id
, participant
, level
);
1353 purple_media_set_output_window(PurpleMedia
*media
, const gchar
*session_id
,
1354 const gchar
*participant
, gulong window_id
)
1357 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1359 return purple_media_manager_set_output_window(media
->priv
->manager
,
1360 media
, session_id
, participant
, window_id
);
1367 purple_media_remove_output_windows(PurpleMedia
*media
)
1370 GList
*iter
= media
->priv
->streams
;
1371 for (; iter
; iter
= g_list_next(iter
)) {
1372 PurpleMediaStream
*stream
= iter
->data
;
1373 purple_media_manager_remove_output_windows(
1374 media
->priv
->manager
, media
,
1375 stream
->session
->id
, stream
->participant
);
1378 iter
= purple_media_get_session_ids(media
);
1379 for (; iter
; iter
= g_list_delete_link(iter
, iter
)) {
1380 gchar
*session_name
= iter
->data
;
1381 purple_media_manager_remove_output_windows(
1382 media
->priv
->manager
, media
,
1383 session_name
, NULL
);
1388 #ifdef USE_GSTREAMER
1390 purple_media_get_tee(PurpleMedia
*media
,
1391 const gchar
*session_id
, const gchar
*participant
)
1394 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1396 if (PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
))
1397 return purple_media_backend_fs2_get_tee(
1398 PURPLE_MEDIA_BACKEND_FS2(
1399 media
->priv
->backend
),
1400 session_id
, participant
);
1401 g_return_val_if_reached(NULL
);
1406 #endif /* USE_GSTREAMER */