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
25 #include "media/backend-iface.h"
26 #include "mediamanager.h"
31 #include "media/backend-fs2.h"
32 #include "media-gst.h"
33 #endif /* USE_GSTREAMER */
36 /** @copydoc _PurpleMediaSession */
37 typedef struct _PurpleMediaSession PurpleMediaSession
;
38 /** @copydoc _PurpleMediaStream */
39 typedef struct _PurpleMediaStream PurpleMediaStream
;
41 struct _PurpleMediaSession
45 PurpleMediaSessionType type
;
49 struct _PurpleMediaStream
51 PurpleMediaSession
*session
;
54 GList
*local_candidates
;
55 GList
*remote_candidates
;
59 gboolean candidates_prepared
;
61 GList
*active_local_candidates
;
62 GList
*active_remote_candidates
;
66 struct _PurpleMediaPrivate
69 PurpleMediaManager
*manager
;
70 PurpleAccount
*account
;
71 PurpleMediaBackend
*backend
;
72 gchar
*conference_type
;
74 gpointer protocol_data
;
76 GHashTable
*sessions
; /* PurpleMediaSession table */
78 GList
*streams
; /* PurpleMediaStream table */
85 static void purple_media_class_init (PurpleMediaClass
*klass
);
86 static void purple_media_init (PurpleMedia
*media
);
87 static void purple_media_dispose (GObject
*object
);
88 static void purple_media_finalize (GObject
*object
);
89 static void purple_media_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
);
90 static void purple_media_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
);
92 static void purple_media_new_local_candidate_cb(PurpleMediaBackend
*backend
,
93 const gchar
*sess_id
, const gchar
*participant
,
94 PurpleMediaCandidate
*candidate
, PurpleMedia
*media
);
95 static void purple_media_candidates_prepared_cb(PurpleMediaBackend
*backend
,
96 const gchar
*sess_id
, const gchar
*name
, PurpleMedia
*media
);
97 static void purple_media_candidate_pair_established_cb(
98 PurpleMediaBackend
*backend
,
99 const gchar
*sess_id
, const gchar
*name
,
100 PurpleMediaCandidate
*local_candidate
,
101 PurpleMediaCandidate
*remote_candidate
,
103 static void purple_media_codecs_changed_cb(PurpleMediaBackend
*backend
,
104 const gchar
*sess_id
, PurpleMedia
*media
);
115 CANDIDATE_PAIR_ESTABLISHED
,
118 static guint purple_media_signals
[LAST_SIGNAL
] = {0};
125 PROP_CONFERENCE_TYPE
,
130 G_DEFINE_TYPE_WITH_PRIVATE(PurpleMedia
, purple_media
, G_TYPE_OBJECT
);
133 purple_media_get_type()
141 purple_media_class_init (PurpleMediaClass
*klass
)
143 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
145 gobject_class
->dispose
= purple_media_dispose
;
146 gobject_class
->finalize
= purple_media_finalize
;
147 gobject_class
->set_property
= purple_media_set_property
;
148 gobject_class
->get_property
= purple_media_get_property
;
150 g_object_class_install_property(gobject_class
, PROP_MANAGER
,
151 g_param_spec_object("manager",
152 "Purple Media Manager",
153 "The media manager that contains this media session.",
154 PURPLE_TYPE_MEDIA_MANAGER
,
155 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
156 G_PARAM_STATIC_STRINGS
));
159 * This one should be PURPLE_TYPE_MEDIA_BACKEND, but it doesn't
160 * like interfaces because they "aren't GObjects"
162 g_object_class_install_property(gobject_class
, PROP_BACKEND
,
163 g_param_spec_object("backend",
164 "Purple Media Backend",
165 "The backend object this media object uses.",
167 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
));
169 g_object_class_install_property(gobject_class
, PROP_ACCOUNT
,
170 g_param_spec_object("account", "PurpleAccount",
171 "The account this media session is on.",
173 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
174 G_PARAM_STATIC_STRINGS
));
176 g_object_class_install_property(gobject_class
, PROP_CONFERENCE_TYPE
,
177 g_param_spec_string("conference-type",
179 "The type of conference that this media object "
180 "has been created to provide.",
182 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
183 G_PARAM_STATIC_STRINGS
));
185 g_object_class_install_property(gobject_class
, PROP_INITIATOR
,
186 g_param_spec_boolean("initiator",
188 "If the local user initiated the conference.",
190 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
191 G_PARAM_STATIC_STRINGS
));
193 g_object_class_install_property(gobject_class
, PROP_PROTOCOL_DATA
,
194 g_param_spec_pointer("protocol-data",
196 "Data the protocol set on the media session.",
197 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
));
199 purple_media_signals
[S_ERROR
] = g_signal_new("error", G_TYPE_FROM_CLASS(klass
),
200 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
201 G_TYPE_NONE
, 1, G_TYPE_STRING
);
202 purple_media_signals
[CANDIDATES_PREPARED
] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass
),
203 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
204 G_TYPE_NONE
, 2, G_TYPE_STRING
,
206 purple_media_signals
[CODECS_CHANGED
] = g_signal_new("codecs-changed", G_TYPE_FROM_CLASS(klass
),
207 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
208 G_TYPE_NONE
, 1, G_TYPE_STRING
);
209 purple_media_signals
[LEVEL
] = g_signal_new("level", G_TYPE_FROM_CLASS(klass
),
210 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
211 G_TYPE_NONE
, 3, G_TYPE_STRING
,
212 G_TYPE_STRING
, G_TYPE_DOUBLE
);
213 purple_media_signals
[NEW_CANDIDATE
] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass
),
214 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
215 G_TYPE_NONE
, 3, G_TYPE_POINTER
,
216 G_TYPE_POINTER
, PURPLE_TYPE_MEDIA_CANDIDATE
);
217 purple_media_signals
[STATE_CHANGED
] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass
),
218 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
219 G_TYPE_NONE
, 3, PURPLE_MEDIA_TYPE_STATE
,
220 G_TYPE_STRING
, G_TYPE_STRING
);
221 purple_media_signals
[STREAM_INFO
] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass
),
222 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
223 G_TYPE_NONE
, 4, PURPLE_MEDIA_TYPE_INFO_TYPE
,
224 G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
225 purple_media_signals
[CANDIDATE_PAIR_ESTABLISHED
] = g_signal_new("candidate-pair-established", G_TYPE_FROM_CLASS(klass
),
226 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
227 G_TYPE_NONE
, 4, G_TYPE_POINTER
, G_TYPE_POINTER
,
228 PURPLE_TYPE_MEDIA_CANDIDATE
, PURPLE_TYPE_MEDIA_CANDIDATE
);
233 purple_media_init (PurpleMedia
*media
)
235 media
->priv
= purple_media_get_instance_private(media
);
236 memset(media
->priv
, 0, sizeof(*media
->priv
));
240 purple_media_stream_free(PurpleMediaStream
*stream
)
245 g_free(stream
->participant
);
247 if (stream
->local_candidates
)
248 purple_media_candidate_list_free(stream
->local_candidates
);
249 if (stream
->remote_candidates
)
250 purple_media_candidate_list_free(stream
->remote_candidates
);
252 if (stream
->active_local_candidates
)
253 purple_media_candidate_list_free(
254 stream
->active_local_candidates
);
255 if (stream
->active_remote_candidates
)
256 purple_media_candidate_list_free(
257 stream
->active_remote_candidates
);
263 purple_media_session_free(PurpleMediaSession
*session
)
273 purple_media_dispose(GObject
*media
)
275 PurpleMediaPrivate
*priv
=
276 purple_media_get_instance_private(PURPLE_MEDIA(media
));
278 purple_debug_info("media","purple_media_dispose\n");
280 purple_media_manager_remove_media(priv
->manager
, PURPLE_MEDIA(media
));
283 g_object_unref(priv
->backend
);
284 priv
->backend
= NULL
;
288 g_object_unref(priv
->manager
);
289 priv
->manager
= NULL
;
292 G_OBJECT_CLASS(purple_media_parent_class
)->dispose(media
);
296 purple_media_finalize(GObject
*media
)
298 PurpleMediaPrivate
*priv
=
299 purple_media_get_instance_private(PURPLE_MEDIA(media
));
300 purple_debug_info("media","purple_media_finalize\n");
302 for (; priv
->streams
; priv
->streams
= g_list_delete_link(priv
->streams
, priv
->streams
))
303 purple_media_stream_free(priv
->streams
->data
);
305 for (; priv
->participants
; priv
->participants
= g_list_delete_link(
306 priv
->participants
, priv
->participants
))
307 g_free(priv
->participants
->data
);
309 if (priv
->sessions
) {
310 GList
*sessions
= g_hash_table_get_values(priv
->sessions
);
311 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
312 purple_media_session_free(sessions
->data
);
314 g_hash_table_destroy(priv
->sessions
);
317 G_OBJECT_CLASS(purple_media_parent_class
)->finalize(media
);
321 purple_media_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
324 g_return_if_fail(PURPLE_IS_MEDIA(object
));
326 media
= PURPLE_MEDIA(object
);
330 media
->priv
->manager
= g_value_dup_object(value
);
333 media
->priv
->account
= g_value_get_object(value
);
335 case PROP_CONFERENCE_TYPE
:
336 media
->priv
->conference_type
=
337 g_value_dup_string(value
);
338 media
->priv
->backend
= g_object_new(
339 purple_media_manager_get_backend_type(
340 purple_media_manager_get()),
342 media
->priv
->conference_type
,
345 g_signal_connect(media
->priv
->backend
,
346 "active-candidate-pair",
348 purple_media_candidate_pair_established_cb
),
350 g_signal_connect(media
->priv
->backend
,
351 "candidates-prepared",
353 purple_media_candidates_prepared_cb
),
355 g_signal_connect(media
->priv
->backend
,
358 purple_media_codecs_changed_cb
),
360 g_signal_connect(media
->priv
->backend
,
363 purple_media_new_local_candidate_cb
),
367 media
->priv
->initiator
= g_value_get_boolean(value
);
369 case PROP_PROTOCOL_DATA
:
370 media
->priv
->protocol_data
= g_value_get_pointer(value
);
373 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
379 purple_media_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
382 g_return_if_fail(PURPLE_IS_MEDIA(object
));
384 media
= PURPLE_MEDIA(object
);
388 g_value_set_object(value
, media
->priv
->manager
);
391 g_value_set_object(value
, media
->priv
->backend
);
394 g_value_set_object(value
, media
->priv
->account
);
396 case PROP_CONFERENCE_TYPE
:
397 g_value_set_string(value
,
398 media
->priv
->conference_type
);
401 g_value_set_boolean(value
, media
->priv
->initiator
);
403 case PROP_PROTOCOL_DATA
:
404 g_value_set_pointer(value
, media
->priv
->protocol_data
);
407 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
413 static PurpleMediaSession
*
414 purple_media_get_session(PurpleMedia
*media
, const gchar
*sess_id
)
416 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
417 return (PurpleMediaSession
*) (media
->priv
->sessions
) ?
418 g_hash_table_lookup(media
->priv
->sessions
, sess_id
) : NULL
;
421 static PurpleMediaStream
*
422 purple_media_get_stream(PurpleMedia
*media
, const gchar
*session
, const gchar
*participant
)
426 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
428 streams
= media
->priv
->streams
;
430 for (; streams
; streams
= g_list_next(streams
)) {
431 PurpleMediaStream
*stream
= streams
->data
;
432 if (purple_strequal(stream
->session
->id
, session
) &&
433 purple_strequal(stream
->participant
, participant
))
441 purple_media_get_streams(PurpleMedia
*media
, const gchar
*session
,
442 const gchar
*participant
)
447 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
449 streams
= media
->priv
->streams
;
451 for (; streams
; streams
= g_list_next(streams
)) {
452 PurpleMediaStream
*stream
= streams
->data
;
453 if ((session
== NULL
||
454 purple_strequal(stream
->session
->id
, session
)) &&
455 (participant
== NULL
||
456 purple_strequal(stream
->participant
, participant
)))
457 ret
= g_list_append(ret
, stream
);
464 purple_media_add_session(PurpleMedia
*media
, PurpleMediaSession
*session
)
466 g_return_if_fail(PURPLE_IS_MEDIA(media
));
467 g_return_if_fail(session
!= NULL
);
469 if (!media
->priv
->sessions
) {
470 purple_debug_info("media", "Creating hash table for sessions\n");
471 media
->priv
->sessions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
474 g_hash_table_insert(media
->priv
->sessions
, g_strdup(session
->id
), session
);
477 static PurpleMediaStream
*
478 purple_media_insert_stream(PurpleMediaSession
*session
,
479 const gchar
*name
, gboolean initiator
)
481 PurpleMediaStream
*media_stream
;
483 g_return_val_if_fail(session
!= NULL
, NULL
);
485 media_stream
= g_new0(PurpleMediaStream
, 1);
486 media_stream
->participant
= g_strdup(name
);
487 media_stream
->session
= session
;
488 media_stream
->initiator
= initiator
;
490 session
->media
->priv
->streams
=
491 g_list_append(session
->media
->priv
->streams
, media_stream
);
497 purple_media_insert_local_candidate(PurpleMediaSession
*session
, const gchar
*name
,
498 PurpleMediaCandidate
*candidate
)
500 PurpleMediaStream
*stream
;
502 g_return_if_fail(session
!= NULL
);
504 stream
= purple_media_get_stream(session
->media
, session
->id
, name
);
505 stream
->local_candidates
= g_list_append(stream
->local_candidates
, candidate
);
510 purple_media_get_session_ids(PurpleMedia
*media
)
513 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
514 return media
->priv
->sessions
!= NULL
?
515 g_hash_table_get_keys(media
->priv
->sessions
) : NULL
;
523 purple_media_get_src(PurpleMedia
*media
, const gchar
*sess_id
)
525 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
527 if (PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
))
528 return purple_media_backend_fs2_get_src(
529 PURPLE_MEDIA_BACKEND_FS2(
530 media
->priv
->backend
), sess_id
);
532 g_return_val_if_reached(NULL
);
537 purple_media_get_account(PurpleMedia
*media
)
540 PurpleAccount
*account
;
541 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
542 g_object_get(G_OBJECT(media
), "account", &account
, NULL
);
550 purple_media_get_protocol_data(PurpleMedia
*media
)
553 gpointer protocol_data
;
554 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
555 g_object_get(G_OBJECT(media
), "protocol-data", &protocol_data
, NULL
);
556 return protocol_data
;
563 purple_media_set_protocol_data(PurpleMedia
*media
, gpointer protocol_data
)
566 g_return_if_fail(PURPLE_IS_MEDIA(media
));
567 g_object_set(G_OBJECT(media
), "protocol-data", protocol_data
, NULL
);
572 purple_media_error(PurpleMedia
*media
, const gchar
*error
, ...)
578 g_return_if_fail(PURPLE_IS_MEDIA(media
));
580 va_start(args
, error
);
581 message
= g_strdup_vprintf(error
, args
);
584 purple_debug_error("media", "%s\n", message
);
585 g_signal_emit(media
, purple_media_signals
[S_ERROR
], 0, message
);
592 purple_media_end(PurpleMedia
*media
,
593 const gchar
*session_id
, const gchar
*participant
)
596 GList
*iter
, *sessions
= NULL
, *participants
= NULL
;
598 g_return_if_fail(PURPLE_IS_MEDIA(media
));
600 iter
= purple_media_get_streams(media
, session_id
, participant
);
602 /* Free matching streams */
603 for (; iter
; iter
= g_list_delete_link(iter
, iter
)) {
604 PurpleMediaStream
*stream
= iter
->data
;
606 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
607 0, PURPLE_MEDIA_STATE_END
,
608 stream
->session
->id
, stream
->participant
);
610 media
->priv
->streams
=
611 g_list_remove(media
->priv
->streams
, stream
);
613 if (g_list_find(sessions
, stream
->session
) == NULL
)
614 sessions
= g_list_prepend(sessions
, stream
->session
);
616 if (g_list_find_custom(participants
, stream
->participant
,
617 (GCompareFunc
)strcmp
) == NULL
)
618 participants
= g_list_prepend(participants
,
619 g_strdup(stream
->participant
));
621 purple_media_stream_free(stream
);
624 iter
= media
->priv
->streams
;
626 /* Reduce to list of sessions to remove */
627 for (; iter
; iter
= g_list_next(iter
)) {
628 PurpleMediaStream
*stream
= iter
->data
;
630 sessions
= g_list_remove(sessions
, stream
->session
);
633 /* Free sessions with no streams left */
634 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
635 PurpleMediaSession
*session
= sessions
->data
;
637 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
638 0, PURPLE_MEDIA_STATE_END
,
641 g_hash_table_remove(media
->priv
->sessions
, session
->id
);
642 purple_media_session_free(session
);
645 iter
= media
->priv
->streams
;
647 /* Reduce to list of participants to remove */
648 for (; iter
; iter
= g_list_next(iter
)) {
649 PurpleMediaStream
*stream
= iter
->data
;
652 tmp
= g_list_find_custom(participants
,
653 stream
->participant
, (GCompareFunc
)strcmp
);
657 participants
= g_list_delete_link(participants
, tmp
);
661 /* Remove participants with no streams left (just emit the signal) */
662 for (; participants
; participants
=
663 g_list_delete_link(participants
, participants
)) {
664 gchar
*participant
= participants
->data
;
665 GList
*link
= g_list_find_custom(media
->priv
->participants
,
666 participant
, (GCompareFunc
)strcmp
);
668 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
669 0, PURPLE_MEDIA_STATE_END
,
674 media
->priv
->participants
= g_list_delete_link(
675 media
->priv
->participants
, link
);
681 /* Free the conference if no sessions left */
682 if (media
->priv
->sessions
!= NULL
&&
683 g_hash_table_size(media
->priv
->sessions
) == 0) {
684 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
685 0, PURPLE_MEDIA_STATE_END
,
687 g_object_unref(media
);
694 purple_media_stream_info(PurpleMedia
*media
, PurpleMediaInfoType type
,
695 const gchar
*session_id
, const gchar
*participant
,
699 g_return_if_fail(PURPLE_IS_MEDIA(media
));
701 if (type
== PURPLE_MEDIA_INFO_ACCEPT
) {
702 GList
*streams
, *sessions
= NULL
, *participants
= NULL
;
704 g_return_if_fail(PURPLE_IS_MEDIA(media
));
706 streams
= purple_media_get_streams(media
,
707 session_id
, participant
);
709 /* Emit stream acceptance */
710 for (; streams
; streams
=
711 g_list_delete_link(streams
, streams
)) {
712 PurpleMediaStream
*stream
= streams
->data
;
714 stream
->accepted
= TRUE
;
717 purple_media_signals
[STREAM_INFO
],
718 0, type
, stream
->session
->id
,
719 stream
->participant
, local
);
721 if (g_list_find(sessions
, stream
->session
) == NULL
)
722 sessions
= g_list_prepend(sessions
,
725 if (g_list_find_custom(participants
,
727 (GCompareFunc
)strcmp
) == NULL
)
728 participants
= g_list_prepend(participants
,
729 g_strdup(stream
->participant
));
732 /* Emit session acceptance */
733 for (; sessions
; sessions
=
734 g_list_delete_link(sessions
, sessions
)) {
735 PurpleMediaSession
*session
= sessions
->data
;
737 if (purple_media_accepted(media
, session
->id
, NULL
))
738 g_signal_emit(media
, purple_media_signals
[
740 PURPLE_MEDIA_INFO_ACCEPT
,
741 session
->id
, NULL
, local
);
744 /* Emit participant acceptance */
745 for (; participants
; participants
= g_list_delete_link(
746 participants
, participants
)) {
747 gchar
*participant
= participants
->data
;
749 if (purple_media_accepted(media
, NULL
, participant
))
750 g_signal_emit(media
, purple_media_signals
[
752 PURPLE_MEDIA_INFO_ACCEPT
,
753 NULL
, participant
, local
);
758 /* Emit conference acceptance */
759 if (purple_media_accepted(media
, NULL
, NULL
))
761 purple_media_signals
[STREAM_INFO
],
762 0, PURPLE_MEDIA_INFO_ACCEPT
,
766 } else if (type
== PURPLE_MEDIA_INFO_HANGUP
||
767 type
== PURPLE_MEDIA_INFO_REJECT
) {
770 g_return_if_fail(PURPLE_IS_MEDIA(media
));
772 streams
= purple_media_get_streams(media
,
773 session_id
, participant
);
775 /* Emit for stream */
776 for (; streams
; streams
=
777 g_list_delete_link(streams
, streams
)) {
778 PurpleMediaStream
*stream
= streams
->data
;
781 purple_media_signals
[STREAM_INFO
],
782 0, type
, stream
->session
->id
,
783 stream
->participant
, local
);
786 if (session_id
!= NULL
&& participant
!= NULL
) {
787 /* Everything that needs to be emitted has been */
788 } else if (session_id
== NULL
&& participant
== NULL
) {
789 /* Emit for everything in the conference */
790 GList
*sessions
= NULL
;
791 GList
*participants
= media
->priv
->participants
;
793 if (media
->priv
->sessions
!= NULL
)
794 sessions
= g_hash_table_get_values(
795 media
->priv
->sessions
);
797 /* Emit for sessions */
798 for (; sessions
; sessions
= g_list_delete_link(
799 sessions
, sessions
)) {
800 PurpleMediaSession
*session
= sessions
->data
;
802 g_signal_emit(media
, purple_media_signals
[
803 STREAM_INFO
], 0, type
,
804 session
->id
, NULL
, local
);
807 /* Emit for participants */
808 for (; participants
; participants
=
809 g_list_next(participants
)) {
810 gchar
*participant
= participants
->data
;
812 g_signal_emit(media
, purple_media_signals
[
813 STREAM_INFO
], 0, type
,
814 NULL
, participant
, local
);
817 /* Emit for conference */
819 purple_media_signals
[STREAM_INFO
],
820 0, type
, NULL
, NULL
, local
);
821 } else if (session_id
!= NULL
) {
822 /* Emit just the specific session */
823 PurpleMediaSession
*session
=
824 purple_media_get_session(
827 if (session
== NULL
) {
828 purple_debug_warning("media",
829 "Couldn't find session"
830 " to hangup/reject.\n");
832 g_signal_emit(media
, purple_media_signals
[
833 STREAM_INFO
], 0, type
,
834 session
->id
, NULL
, local
);
836 } else if (participant
!= NULL
) {
837 /* Emit just the specific participant */
838 if (!g_list_find_custom(media
->priv
->participants
,
839 participant
, (GCompareFunc
)strcmp
)) {
840 purple_debug_warning("media",
841 "Couldn't find participant"
842 " to hangup/reject.\n");
844 g_signal_emit(media
, purple_media_signals
[
845 STREAM_INFO
], 0, type
, NULL
,
850 purple_media_end(media
, session_id
, participant
);
854 g_signal_emit(media
, purple_media_signals
[STREAM_INFO
],
855 0, type
, session_id
, participant
, local
);
860 purple_media_set_params(PurpleMedia
*media
,
861 guint num_params
, GParameter
*params
)
864 g_return_if_fail(PURPLE_IS_MEDIA(media
));
866 purple_media_backend_set_params(media
->priv
->backend
, num_params
, params
);
871 purple_media_get_available_params(PurpleMedia
*media
)
873 static const gchar
*NULL_ARRAY
[] = { NULL
};
875 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL_ARRAY
);
877 return purple_media_backend_get_available_params(media
->priv
->backend
);
884 purple_media_param_is_supported(PurpleMedia
*media
, const gchar
*param
)
887 const gchar
**params
;
889 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
890 g_return_val_if_fail(param
!= NULL
, FALSE
);
892 params
= purple_media_backend_get_available_params(media
->priv
->backend
);
893 for (; *params
!= NULL
; ++params
)
894 if (purple_strequal(*params
, param
))
902 purple_media_new_local_candidate_cb(PurpleMediaBackend
*backend
,
903 const gchar
*sess_id
, const gchar
*participant
,
904 PurpleMediaCandidate
*candidate
, PurpleMedia
*media
)
906 PurpleMediaSession
*session
=
907 purple_media_get_session(media
, sess_id
);
909 purple_media_insert_local_candidate(session
, participant
,
910 purple_media_candidate_copy(candidate
));
912 g_signal_emit(session
->media
, purple_media_signals
[NEW_CANDIDATE
],
913 0, session
->id
, participant
, candidate
);
917 purple_media_candidates_prepared_cb(PurpleMediaBackend
*backend
,
918 const gchar
*sess_id
, const gchar
*name
, PurpleMedia
*media
)
920 PurpleMediaStream
*stream_data
;
922 g_return_if_fail(PURPLE_IS_MEDIA(media
));
924 stream_data
= purple_media_get_stream(media
, sess_id
, name
);
925 stream_data
->candidates_prepared
= TRUE
;
927 g_signal_emit(media
, purple_media_signals
[CANDIDATES_PREPARED
],
931 /* callback called when a pair of transport candidates (local and remote)
932 * has been established */
934 purple_media_candidate_pair_established_cb(PurpleMediaBackend
*backend
,
935 const gchar
*sess_id
, const gchar
*name
,
936 PurpleMediaCandidate
*local_candidate
,
937 PurpleMediaCandidate
*remote_candidate
,
940 PurpleMediaStream
*stream
;
944 g_return_if_fail(PURPLE_IS_MEDIA(media
));
946 stream
= purple_media_get_stream(media
, sess_id
, name
);
947 id
= purple_media_candidate_get_component_id(local_candidate
);
949 iter
= stream
->active_local_candidates
;
950 for(; iter
; iter
= g_list_next(iter
)) {
951 PurpleMediaCandidate
*c
= iter
->data
;
952 if (id
== purple_media_candidate_get_component_id(c
)) {
954 stream
->active_local_candidates
=
955 g_list_delete_link(iter
, iter
);
956 stream
->active_local_candidates
= g_list_prepend(
957 stream
->active_local_candidates
,
958 purple_media_candidate_copy(
964 stream
->active_local_candidates
= g_list_prepend(
965 stream
->active_local_candidates
,
966 purple_media_candidate_copy(
969 id
= purple_media_candidate_get_component_id(local_candidate
);
971 iter
= stream
->active_remote_candidates
;
972 for(; iter
; iter
= g_list_next(iter
)) {
973 PurpleMediaCandidate
*c
= iter
->data
;
974 if (id
== purple_media_candidate_get_component_id(c
)) {
976 stream
->active_remote_candidates
=
977 g_list_delete_link(iter
, iter
);
978 stream
->active_remote_candidates
= g_list_prepend(
979 stream
->active_remote_candidates
,
980 purple_media_candidate_copy(
986 stream
->active_remote_candidates
= g_list_prepend(
987 stream
->active_remote_candidates
,
988 purple_media_candidate_copy(
991 g_signal_emit(media
, purple_media_signals
[CANDIDATE_PAIR_ESTABLISHED
],
992 0, sess_id
, name
, local_candidate
, remote_candidate
);
993 purple_debug_info("media", "candidate pair established\n");
997 purple_media_codecs_changed_cb(PurpleMediaBackend
*backend
,
998 const gchar
*sess_id
, PurpleMedia
*media
)
1000 g_signal_emit(media
, purple_media_signals
[CODECS_CHANGED
], 0, sess_id
);
1005 purple_media_add_stream(PurpleMedia
*media
, const gchar
*sess_id
,
1006 const gchar
*who
, PurpleMediaSessionType type
,
1007 gboolean initiator
, const gchar
*transmitter
,
1008 guint num_params
, GParameter
*params
)
1011 PurpleMediaSession
*session
;
1013 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1015 if (!purple_media_backend_add_stream(media
->priv
->backend
,
1016 sess_id
, who
, type
, initiator
, transmitter
,
1017 num_params
, params
)) {
1018 purple_debug_error("media", "Error adding stream.\n");
1022 session
= purple_media_get_session(media
, sess_id
);
1025 session
= g_new0(PurpleMediaSession
, 1);
1026 session
->id
= g_strdup(sess_id
);
1027 session
->media
= media
;
1028 session
->type
= type
;
1029 session
->initiator
= initiator
;
1031 purple_media_add_session(media
, session
);
1032 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
1033 0, PURPLE_MEDIA_STATE_NEW
,
1037 if (!g_list_find_custom(media
->priv
->participants
,
1038 who
, (GCompareFunc
)strcmp
)) {
1039 media
->priv
->participants
= g_list_prepend(
1040 media
->priv
->participants
, g_strdup(who
));
1042 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
], 0,
1043 PURPLE_MEDIA_STATE_NEW
, NULL
, who
);
1046 if (purple_media_get_stream(media
, sess_id
, who
) == NULL
) {
1047 purple_media_insert_stream(session
, who
, initiator
);
1049 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
1050 0, PURPLE_MEDIA_STATE_NEW
,
1060 PurpleMediaManager
*
1061 purple_media_get_manager(PurpleMedia
*media
)
1063 PurpleMediaManager
*ret
;
1064 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1065 g_object_get(media
, "manager", &ret
, NULL
);
1069 PurpleMediaSessionType
1070 purple_media_get_session_type(PurpleMedia
*media
, const gchar
*sess_id
)
1073 PurpleMediaSession
*session
;
1074 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), PURPLE_MEDIA_NONE
);
1075 session
= purple_media_get_session(media
, sess_id
);
1076 return session
->type
;
1078 return PURPLE_MEDIA_NONE
;
1081 /* XXX: Should wait until codecs-ready is TRUE before using this function */
1083 purple_media_get_codecs(PurpleMedia
*media
, const gchar
*sess_id
)
1086 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1088 return purple_media_backend_get_codecs(media
->priv
->backend
, sess_id
);
1095 purple_media_get_local_candidates(PurpleMedia
*media
, const gchar
*sess_id
,
1096 const gchar
*participant
)
1099 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1101 return purple_media_backend_get_local_candidates(media
->priv
->backend
,
1102 sess_id
, participant
);
1109 purple_media_add_remote_candidates(PurpleMedia
*media
, const gchar
*sess_id
,
1110 const gchar
*participant
,
1111 GList
*remote_candidates
)
1114 PurpleMediaStream
*stream
;
1116 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1117 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1119 if (stream
== NULL
) {
1120 purple_debug_error("media",
1121 "purple_media_add_remote_candidates: "
1122 "couldn't find stream %s %s.\n",
1123 sess_id
? sess_id
: "(null)",
1124 participant
? participant
: "(null)");
1128 stream
->remote_candidates
= g_list_concat(stream
->remote_candidates
,
1129 purple_media_candidate_list_copy(remote_candidates
));
1131 purple_media_backend_add_remote_candidates(media
->priv
->backend
,
1132 sess_id
, participant
, remote_candidates
);
1137 purple_media_get_active_local_candidates(PurpleMedia
*media
,
1138 const gchar
*sess_id
, const gchar
*participant
)
1141 PurpleMediaStream
*stream
;
1142 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1143 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1144 return purple_media_candidate_list_copy(
1145 stream
->active_local_candidates
);
1152 purple_media_get_active_remote_candidates(PurpleMedia
*media
,
1153 const gchar
*sess_id
, const gchar
*participant
)
1156 PurpleMediaStream
*stream
;
1157 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1158 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1159 return purple_media_candidate_list_copy(
1160 stream
->active_remote_candidates
);
1167 purple_media_set_remote_codecs(PurpleMedia
*media
, const gchar
*sess_id
,
1168 const gchar
*participant
, GList
*codecs
)
1171 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1173 return purple_media_backend_set_remote_codecs(media
->priv
->backend
,
1174 sess_id
, participant
, codecs
);
1181 purple_media_candidates_prepared(PurpleMedia
*media
,
1182 const gchar
*session_id
, const gchar
*participant
)
1186 gboolean prepared
= TRUE
;
1188 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1190 streams
= purple_media_get_streams(media
, session_id
, participant
);
1192 for (; streams
; streams
= g_list_delete_link(streams
, streams
)) {
1193 PurpleMediaStream
*stream
= streams
->data
;
1194 if (stream
->candidates_prepared
== FALSE
) {
1195 g_list_free(streams
);
1208 purple_media_set_send_codec(PurpleMedia
*media
, const gchar
*sess_id
, PurpleMediaCodec
*codec
)
1211 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1213 return purple_media_backend_set_send_codec(
1214 media
->priv
->backend
, sess_id
, codec
);
1221 purple_media_set_encryption_parameters(PurpleMedia
*media
, const gchar
*sess_id
,
1222 const gchar
*cipher
, const gchar
*auth
,
1223 const gchar
*key
, gsize key_len
)
1226 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1227 return purple_media_backend_set_encryption_parameters(media
->priv
->backend
,
1228 sess_id
, cipher
, auth
, key
, key_len
);
1235 purple_media_set_decryption_parameters(PurpleMedia
*media
, const gchar
*sess_id
,
1236 const gchar
*participant
, const gchar
*cipher
,
1237 const gchar
*auth
, const gchar
*key
, gsize key_len
)
1240 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1241 return purple_media_backend_set_decryption_parameters(media
->priv
->backend
,
1242 sess_id
, participant
, cipher
, auth
, key
, key_len
);
1249 purple_media_codecs_ready(PurpleMedia
*media
, const gchar
*sess_id
)
1252 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1254 return purple_media_backend_codecs_ready(
1255 media
->priv
->backend
, sess_id
);
1262 purple_media_set_send_rtcp_mux(PurpleMedia
*media
, const gchar
*sess_id
,
1263 const gchar
*participant
, gboolean send_rtcp_mux
)
1266 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1268 return purple_media_backend_set_send_rtcp_mux(media
->priv
->backend
,
1269 sess_id
, participant
, send_rtcp_mux
);
1276 purple_media_is_initiator(PurpleMedia
*media
,
1277 const gchar
*sess_id
, const gchar
*participant
)
1280 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1282 if (sess_id
== NULL
&& participant
== NULL
)
1283 return media
->priv
->initiator
;
1284 else if (sess_id
!= NULL
&& participant
== NULL
) {
1285 PurpleMediaSession
*session
=
1286 purple_media_get_session(media
, sess_id
);
1287 return session
!= NULL
? session
->initiator
: FALSE
;
1288 } else if (sess_id
!= NULL
&& participant
!= NULL
) {
1289 PurpleMediaStream
*stream
= purple_media_get_stream(
1290 media
, sess_id
, participant
);
1291 return stream
!= NULL
? stream
->initiator
: FALSE
;
1298 purple_media_accepted(PurpleMedia
*media
, const gchar
*sess_id
,
1299 const gchar
*participant
)
1302 gboolean accepted
= TRUE
;
1304 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1306 if (sess_id
== NULL
&& participant
== NULL
) {
1307 GList
*streams
= media
->priv
->streams
;
1309 for (; streams
; streams
= g_list_next(streams
)) {
1310 PurpleMediaStream
*stream
= streams
->data
;
1311 if (stream
->accepted
== FALSE
) {
1316 } else if (sess_id
!= NULL
&& participant
== NULL
) {
1317 GList
*streams
= purple_media_get_streams(
1318 media
, sess_id
, NULL
);
1319 for (; streams
; streams
=
1320 g_list_delete_link(streams
, streams
)) {
1321 PurpleMediaStream
*stream
= streams
->data
;
1322 if (stream
->accepted
== FALSE
) {
1323 g_list_free(streams
);
1328 } else if (sess_id
!= NULL
&& participant
!= NULL
) {
1329 PurpleMediaStream
*stream
= purple_media_get_stream(
1330 media
, sess_id
, participant
);
1331 if (stream
== NULL
|| stream
->accepted
== FALSE
)
1341 void purple_media_set_input_volume(PurpleMedia
*media
,
1342 const gchar
*session_id
, double level
)
1345 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1346 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
));
1348 purple_media_backend_fs2_set_input_volume(
1349 PURPLE_MEDIA_BACKEND_FS2(
1350 media
->priv
->backend
),
1355 void purple_media_set_output_volume(PurpleMedia
*media
,
1356 const gchar
*session_id
, const gchar
*participant
,
1360 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1361 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
));
1363 purple_media_backend_fs2_set_output_volume(
1364 PURPLE_MEDIA_BACKEND_FS2(
1365 media
->priv
->backend
),
1366 session_id
, participant
, level
);
1371 purple_media_set_output_window(PurpleMedia
*media
, const gchar
*session_id
,
1372 const gchar
*participant
, gulong window_id
)
1375 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1377 return purple_media_manager_set_output_window(media
->priv
->manager
,
1378 media
, session_id
, participant
, window_id
);
1385 purple_media_remove_output_windows(PurpleMedia
*media
)
1388 GList
*iter
= media
->priv
->streams
;
1389 for (; iter
; iter
= g_list_next(iter
)) {
1390 PurpleMediaStream
*stream
= iter
->data
;
1391 purple_media_manager_remove_output_windows(
1392 media
->priv
->manager
, media
,
1393 stream
->session
->id
, stream
->participant
);
1396 iter
= purple_media_get_session_ids(media
);
1397 for (; iter
; iter
= g_list_delete_link(iter
, iter
)) {
1398 gchar
*session_name
= iter
->data
;
1399 purple_media_manager_remove_output_windows(
1400 media
->priv
->manager
, media
,
1401 session_name
, NULL
);
1408 purple_media_get_tee(PurpleMedia
*media
,
1409 const gchar
*session_id
, const gchar
*participant
)
1411 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1413 if (PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
))
1414 return purple_media_backend_fs2_get_tee(
1415 PURPLE_MEDIA_BACKEND_FS2(
1416 media
->priv
->backend
),
1417 session_id
, participant
);
1418 g_return_val_if_reached(NULL
);
1423 purple_media_send_dtmf(PurpleMedia
*media
, const gchar
*session_id
,
1424 gchar dtmf
, guint8 volume
, guint16 duration
)
1427 PurpleMediaBackendInterface
*backend_iface
= NULL
;
1431 backend_iface
= PURPLE_MEDIA_BACKEND_GET_INTERFACE(media
->priv
->backend
);
1436 else if (dtmf
== 'b')
1438 else if (dtmf
== 'c')
1440 else if (dtmf
== 'd')
1443 g_return_val_if_fail(strchr("0123456789ABCD#*", dtmf
), FALSE
);
1445 if (backend_iface
&& backend_iface
->send_dtmf
1446 && backend_iface
->send_dtmf(media
->priv
->backend
,
1447 session_id
, dtmf
, volume
, duration
))