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 "marshallers.h"
33 #include "media-gst.h"
34 #endif /* USE_GSTREAMER */
37 /** @copydoc _PurpleMediaSession */
38 typedef struct _PurpleMediaSession PurpleMediaSession
;
39 /** @copydoc _PurpleMediaStream */
40 typedef struct _PurpleMediaStream PurpleMediaStream
;
42 struct _PurpleMediaSession
46 PurpleMediaSessionType type
;
50 struct _PurpleMediaStream
52 PurpleMediaSession
*session
;
55 GList
*local_candidates
;
56 GList
*remote_candidates
;
60 gboolean candidates_prepared
;
62 GList
*active_local_candidates
;
63 GList
*active_remote_candidates
;
67 struct _PurpleMediaPrivate
70 PurpleMediaManager
*manager
;
71 PurpleAccount
*account
;
72 PurpleMediaBackend
*backend
;
73 gchar
*conference_type
;
75 gpointer protocol_data
;
77 GHashTable
*sessions
; /* PurpleMediaSession table */
79 GList
*streams
; /* PurpleMediaStream table */
86 #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate))
88 static void purple_media_class_init (PurpleMediaClass
*klass
);
89 static void purple_media_init (PurpleMedia
*media
);
90 static void purple_media_dispose (GObject
*object
);
91 static void purple_media_finalize (GObject
*object
);
92 static void purple_media_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
);
93 static void purple_media_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
);
95 static void purple_media_new_local_candidate_cb(PurpleMediaBackend
*backend
,
96 const gchar
*sess_id
, const gchar
*participant
,
97 PurpleMediaCandidate
*candidate
, PurpleMedia
*media
);
98 static void purple_media_candidates_prepared_cb(PurpleMediaBackend
*backend
,
99 const gchar
*sess_id
, const gchar
*name
, PurpleMedia
*media
);
100 static void purple_media_candidate_pair_established_cb(
101 PurpleMediaBackend
*backend
,
102 const gchar
*sess_id
, const gchar
*name
,
103 PurpleMediaCandidate
*local_candidate
,
104 PurpleMediaCandidate
*remote_candidate
,
106 static void purple_media_codecs_changed_cb(PurpleMediaBackend
*backend
,
107 const gchar
*sess_id
, PurpleMedia
*media
);
109 static GObjectClass
*parent_class
= NULL
;
121 CANDIDATE_PAIR_ESTABLISHED
,
124 static guint purple_media_signals
[LAST_SIGNAL
] = {0};
131 PROP_CONFERENCE_TYPE
,
139 purple_media_get_type()
142 static GType type
= 0;
145 static const GTypeInfo info
= {
146 sizeof(PurpleMediaClass
),
149 (GClassInitFunc
) purple_media_class_init
,
154 (GInstanceInitFunc
) purple_media_init
,
157 type
= g_type_register_static(G_TYPE_OBJECT
, "PurpleMedia", &info
, 0);
167 purple_media_class_init (PurpleMediaClass
*klass
)
169 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
170 parent_class
= g_type_class_peek_parent(klass
);
172 gobject_class
->dispose
= purple_media_dispose
;
173 gobject_class
->finalize
= purple_media_finalize
;
174 gobject_class
->set_property
= purple_media_set_property
;
175 gobject_class
->get_property
= purple_media_get_property
;
177 g_object_class_install_property(gobject_class
, PROP_MANAGER
,
178 g_param_spec_object("manager",
179 "Purple Media Manager",
180 "The media manager that contains this media session.",
181 PURPLE_TYPE_MEDIA_MANAGER
,
182 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
183 G_PARAM_STATIC_STRINGS
));
186 * This one should be PURPLE_TYPE_MEDIA_BACKEND, but it doesn't
187 * like interfaces because they "aren't GObjects"
189 g_object_class_install_property(gobject_class
, PROP_BACKEND
,
190 g_param_spec_object("backend",
191 "Purple Media Backend",
192 "The backend object this media object uses.",
194 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
));
196 g_object_class_install_property(gobject_class
, PROP_ACCOUNT
,
197 g_param_spec_object("account", "PurpleAccount",
198 "The account this media session is on.",
200 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
201 G_PARAM_STATIC_STRINGS
));
203 g_object_class_install_property(gobject_class
, PROP_CONFERENCE_TYPE
,
204 g_param_spec_string("conference-type",
206 "The type of conference that this media object "
207 "has been created to provide.",
209 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
210 G_PARAM_STATIC_STRINGS
));
212 g_object_class_install_property(gobject_class
, PROP_INITIATOR
,
213 g_param_spec_boolean("initiator",
215 "If the local user initiated the conference.",
217 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
218 G_PARAM_STATIC_STRINGS
));
220 g_object_class_install_property(gobject_class
, PROP_PROTOCOL_DATA
,
221 g_param_spec_pointer("protocol-data",
223 "Data the protocol set on the media session.",
224 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
));
226 purple_media_signals
[S_ERROR
] = g_signal_new("error", G_TYPE_FROM_CLASS(klass
),
227 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
228 g_cclosure_marshal_VOID__STRING
,
229 G_TYPE_NONE
, 1, G_TYPE_STRING
);
230 purple_media_signals
[CANDIDATES_PREPARED
] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass
),
231 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
232 purple_smarshal_VOID__STRING_STRING
,
233 G_TYPE_NONE
, 2, G_TYPE_STRING
,
235 purple_media_signals
[CODECS_CHANGED
] = g_signal_new("codecs-changed", G_TYPE_FROM_CLASS(klass
),
236 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
237 g_cclosure_marshal_VOID__STRING
,
238 G_TYPE_NONE
, 1, G_TYPE_STRING
);
239 purple_media_signals
[LEVEL
] = g_signal_new("level", G_TYPE_FROM_CLASS(klass
),
240 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
241 purple_smarshal_VOID__STRING_STRING_DOUBLE
,
242 G_TYPE_NONE
, 3, G_TYPE_STRING
,
243 G_TYPE_STRING
, G_TYPE_DOUBLE
);
244 purple_media_signals
[NEW_CANDIDATE
] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass
),
245 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
246 purple_smarshal_VOID__POINTER_POINTER_OBJECT
,
247 G_TYPE_NONE
, 3, G_TYPE_POINTER
,
248 G_TYPE_POINTER
, PURPLE_TYPE_MEDIA_CANDIDATE
);
249 purple_media_signals
[STATE_CHANGED
] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass
),
250 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
251 purple_smarshal_VOID__ENUM_STRING_STRING
,
252 G_TYPE_NONE
, 3, PURPLE_MEDIA_TYPE_STATE
,
253 G_TYPE_STRING
, G_TYPE_STRING
);
254 purple_media_signals
[STREAM_INFO
] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass
),
255 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
256 purple_smarshal_VOID__ENUM_STRING_STRING_BOOLEAN
,
257 G_TYPE_NONE
, 4, PURPLE_MEDIA_TYPE_INFO_TYPE
,
258 G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
259 purple_media_signals
[CANDIDATE_PAIR_ESTABLISHED
] = g_signal_new("candidate-pair-established", G_TYPE_FROM_CLASS(klass
),
260 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
,
261 purple_smarshal_VOID__POINTER_POINTER_OBJECT_OBJECT
,
262 G_TYPE_NONE
, 4, G_TYPE_POINTER
, G_TYPE_POINTER
,
263 PURPLE_TYPE_MEDIA_CANDIDATE
, PURPLE_TYPE_MEDIA_CANDIDATE
);
264 g_type_class_add_private(klass
, sizeof(PurpleMediaPrivate
));
269 purple_media_init (PurpleMedia
*media
)
271 media
->priv
= PURPLE_MEDIA_GET_PRIVATE(media
);
272 memset(media
->priv
, 0, sizeof(*media
->priv
));
276 purple_media_stream_free(PurpleMediaStream
*stream
)
281 g_free(stream
->participant
);
283 if (stream
->local_candidates
)
284 purple_media_candidate_list_free(stream
->local_candidates
);
285 if (stream
->remote_candidates
)
286 purple_media_candidate_list_free(stream
->remote_candidates
);
288 if (stream
->active_local_candidates
)
289 purple_media_candidate_list_free(
290 stream
->active_local_candidates
);
291 if (stream
->active_remote_candidates
)
292 purple_media_candidate_list_free(
293 stream
->active_remote_candidates
);
299 purple_media_session_free(PurpleMediaSession
*session
)
309 purple_media_dispose(GObject
*media
)
311 PurpleMediaPrivate
*priv
= PURPLE_MEDIA_GET_PRIVATE(media
);
313 purple_debug_info("media","purple_media_dispose\n");
315 purple_media_manager_remove_media(priv
->manager
, PURPLE_MEDIA(media
));
318 g_object_unref(priv
->backend
);
319 priv
->backend
= NULL
;
323 g_object_unref(priv
->manager
);
324 priv
->manager
= NULL
;
327 G_OBJECT_CLASS(parent_class
)->dispose(media
);
331 purple_media_finalize(GObject
*media
)
333 PurpleMediaPrivate
*priv
= PURPLE_MEDIA_GET_PRIVATE(media
);
334 purple_debug_info("media","purple_media_finalize\n");
336 for (; priv
->streams
; priv
->streams
= g_list_delete_link(priv
->streams
, priv
->streams
))
337 purple_media_stream_free(priv
->streams
->data
);
339 for (; priv
->participants
; priv
->participants
= g_list_delete_link(
340 priv
->participants
, priv
->participants
))
341 g_free(priv
->participants
->data
);
343 if (priv
->sessions
) {
344 GList
*sessions
= g_hash_table_get_values(priv
->sessions
);
345 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
346 purple_media_session_free(sessions
->data
);
348 g_hash_table_destroy(priv
->sessions
);
351 G_OBJECT_CLASS(parent_class
)->finalize(media
);
355 purple_media_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
358 g_return_if_fail(PURPLE_IS_MEDIA(object
));
360 media
= PURPLE_MEDIA(object
);
364 media
->priv
->manager
= g_value_dup_object(value
);
367 media
->priv
->account
= g_value_get_object(value
);
369 case PROP_CONFERENCE_TYPE
:
370 media
->priv
->conference_type
=
371 g_value_dup_string(value
);
372 media
->priv
->backend
= g_object_new(
373 purple_media_manager_get_backend_type(
374 purple_media_manager_get()),
376 media
->priv
->conference_type
,
379 g_signal_connect(media
->priv
->backend
,
380 "active-candidate-pair",
382 purple_media_candidate_pair_established_cb
),
384 g_signal_connect(media
->priv
->backend
,
385 "candidates-prepared",
387 purple_media_candidates_prepared_cb
),
389 g_signal_connect(media
->priv
->backend
,
392 purple_media_codecs_changed_cb
),
394 g_signal_connect(media
->priv
->backend
,
397 purple_media_new_local_candidate_cb
),
401 media
->priv
->initiator
= g_value_get_boolean(value
);
403 case PROP_PROTOCOL_DATA
:
404 media
->priv
->protocol_data
= g_value_get_pointer(value
);
407 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
413 purple_media_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
416 g_return_if_fail(PURPLE_IS_MEDIA(object
));
418 media
= PURPLE_MEDIA(object
);
422 g_value_set_object(value
, media
->priv
->manager
);
425 g_value_set_object(value
, media
->priv
->backend
);
428 g_value_set_object(value
, media
->priv
->account
);
430 case PROP_CONFERENCE_TYPE
:
431 g_value_set_string(value
,
432 media
->priv
->conference_type
);
435 g_value_set_boolean(value
, media
->priv
->initiator
);
437 case PROP_PROTOCOL_DATA
:
438 g_value_set_pointer(value
, media
->priv
->protocol_data
);
441 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
447 static PurpleMediaSession
*
448 purple_media_get_session(PurpleMedia
*media
, const gchar
*sess_id
)
450 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
451 return (PurpleMediaSession
*) (media
->priv
->sessions
) ?
452 g_hash_table_lookup(media
->priv
->sessions
, sess_id
) : NULL
;
455 static PurpleMediaStream
*
456 purple_media_get_stream(PurpleMedia
*media
, const gchar
*session
, const gchar
*participant
)
460 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
462 streams
= media
->priv
->streams
;
464 for (; streams
; streams
= g_list_next(streams
)) {
465 PurpleMediaStream
*stream
= streams
->data
;
466 if (!strcmp(stream
->session
->id
, session
) &&
467 !strcmp(stream
->participant
, participant
))
475 purple_media_get_streams(PurpleMedia
*media
, const gchar
*session
,
476 const gchar
*participant
)
481 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
483 streams
= media
->priv
->streams
;
485 for (; streams
; streams
= g_list_next(streams
)) {
486 PurpleMediaStream
*stream
= streams
->data
;
487 if ((session
== NULL
||
488 !strcmp(stream
->session
->id
, session
)) &&
489 (participant
== NULL
||
490 !strcmp(stream
->participant
, participant
)))
491 ret
= g_list_append(ret
, stream
);
498 purple_media_add_session(PurpleMedia
*media
, PurpleMediaSession
*session
)
500 g_return_if_fail(PURPLE_IS_MEDIA(media
));
501 g_return_if_fail(session
!= NULL
);
503 if (!media
->priv
->sessions
) {
504 purple_debug_info("media", "Creating hash table for sessions\n");
505 media
->priv
->sessions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
508 g_hash_table_insert(media
->priv
->sessions
, g_strdup(session
->id
), session
);
513 purple_media_remove_session(PurpleMedia
*media
, PurpleMediaSession
*session
)
515 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
516 return g_hash_table_remove(media
->priv
->sessions
, session
->id
);
520 static PurpleMediaStream
*
521 purple_media_insert_stream(PurpleMediaSession
*session
,
522 const gchar
*name
, gboolean initiator
)
524 PurpleMediaStream
*media_stream
;
526 g_return_val_if_fail(session
!= NULL
, NULL
);
528 media_stream
= g_new0(PurpleMediaStream
, 1);
529 media_stream
->participant
= g_strdup(name
);
530 media_stream
->session
= session
;
531 media_stream
->initiator
= initiator
;
533 session
->media
->priv
->streams
=
534 g_list_append(session
->media
->priv
->streams
, media_stream
);
540 purple_media_insert_local_candidate(PurpleMediaSession
*session
, const gchar
*name
,
541 PurpleMediaCandidate
*candidate
)
543 PurpleMediaStream
*stream
;
545 g_return_if_fail(session
!= NULL
);
547 stream
= purple_media_get_stream(session
->media
, session
->id
, name
);
548 stream
->local_candidates
= g_list_append(stream
->local_candidates
, candidate
);
553 purple_media_get_session_ids(PurpleMedia
*media
)
556 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
557 return media
->priv
->sessions
!= NULL
?
558 g_hash_table_get_keys(media
->priv
->sessions
) : NULL
;
566 purple_media_get_src(PurpleMedia
*media
, const gchar
*sess_id
)
568 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
570 if (PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
))
571 return purple_media_backend_fs2_get_src(
572 PURPLE_MEDIA_BACKEND_FS2(
573 media
->priv
->backend
), sess_id
);
575 g_return_val_if_reached(NULL
);
580 purple_media_get_account(PurpleMedia
*media
)
583 PurpleAccount
*account
;
584 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
585 g_object_get(G_OBJECT(media
), "account", &account
, NULL
);
593 purple_media_get_protocol_data(PurpleMedia
*media
)
596 gpointer protocol_data
;
597 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
598 g_object_get(G_OBJECT(media
), "protocol-data", &protocol_data
, NULL
);
599 return protocol_data
;
606 purple_media_set_protocol_data(PurpleMedia
*media
, gpointer protocol_data
)
609 g_return_if_fail(PURPLE_IS_MEDIA(media
));
610 g_object_set(G_OBJECT(media
), "protocol-data", protocol_data
, NULL
);
615 purple_media_error(PurpleMedia
*media
, const gchar
*error
, ...)
621 g_return_if_fail(PURPLE_IS_MEDIA(media
));
623 va_start(args
, error
);
624 message
= g_strdup_vprintf(error
, args
);
627 purple_debug_error("media", "%s\n", message
);
628 g_signal_emit(media
, purple_media_signals
[S_ERROR
], 0, message
);
635 purple_media_end(PurpleMedia
*media
,
636 const gchar
*session_id
, const gchar
*participant
)
639 GList
*iter
, *sessions
= NULL
, *participants
= NULL
;
641 g_return_if_fail(PURPLE_IS_MEDIA(media
));
643 iter
= purple_media_get_streams(media
, session_id
, participant
);
645 /* Free matching streams */
646 for (; iter
; iter
= g_list_delete_link(iter
, iter
)) {
647 PurpleMediaStream
*stream
= iter
->data
;
649 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
650 0, PURPLE_MEDIA_STATE_END
,
651 stream
->session
->id
, stream
->participant
);
653 media
->priv
->streams
=
654 g_list_remove(media
->priv
->streams
, stream
);
656 if (g_list_find(sessions
, stream
->session
) == NULL
)
657 sessions
= g_list_prepend(sessions
, stream
->session
);
659 if (g_list_find_custom(participants
, stream
->participant
,
660 (GCompareFunc
)strcmp
) == NULL
)
661 participants
= g_list_prepend(participants
,
662 g_strdup(stream
->participant
));
664 purple_media_stream_free(stream
);
667 iter
= media
->priv
->streams
;
669 /* Reduce to list of sessions to remove */
670 for (; iter
; iter
= g_list_next(iter
)) {
671 PurpleMediaStream
*stream
= iter
->data
;
673 sessions
= g_list_remove(sessions
, stream
->session
);
676 /* Free sessions with no streams left */
677 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
678 PurpleMediaSession
*session
= sessions
->data
;
680 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
681 0, PURPLE_MEDIA_STATE_END
,
684 g_hash_table_remove(media
->priv
->sessions
, session
->id
);
685 purple_media_session_free(session
);
688 iter
= media
->priv
->streams
;
690 /* Reduce to list of participants to remove */
691 for (; iter
; iter
= g_list_next(iter
)) {
692 PurpleMediaStream
*stream
= iter
->data
;
695 tmp
= g_list_find_custom(participants
,
696 stream
->participant
, (GCompareFunc
)strcmp
);
700 participants
= g_list_delete_link(participants
, tmp
);
704 /* Remove participants with no streams left (just emit the signal) */
705 for (; participants
; participants
=
706 g_list_delete_link(participants
, participants
)) {
707 gchar
*participant
= participants
->data
;
708 GList
*link
= g_list_find_custom(media
->priv
->participants
,
709 participant
, (GCompareFunc
)strcmp
);
711 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
712 0, PURPLE_MEDIA_STATE_END
,
717 media
->priv
->participants
= g_list_delete_link(
718 media
->priv
->participants
, link
);
724 /* Free the conference if no sessions left */
725 if (media
->priv
->sessions
!= NULL
&&
726 g_hash_table_size(media
->priv
->sessions
) == 0) {
727 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
728 0, PURPLE_MEDIA_STATE_END
,
730 g_object_unref(media
);
737 purple_media_stream_info(PurpleMedia
*media
, PurpleMediaInfoType type
,
738 const gchar
*session_id
, const gchar
*participant
,
742 g_return_if_fail(PURPLE_IS_MEDIA(media
));
744 if (type
== PURPLE_MEDIA_INFO_ACCEPT
) {
745 GList
*streams
, *sessions
= NULL
, *participants
= NULL
;
747 g_return_if_fail(PURPLE_IS_MEDIA(media
));
749 streams
= purple_media_get_streams(media
,
750 session_id
, participant
);
752 /* Emit stream acceptance */
753 for (; streams
; streams
=
754 g_list_delete_link(streams
, streams
)) {
755 PurpleMediaStream
*stream
= streams
->data
;
757 stream
->accepted
= TRUE
;
760 purple_media_signals
[STREAM_INFO
],
761 0, type
, stream
->session
->id
,
762 stream
->participant
, local
);
764 if (g_list_find(sessions
, stream
->session
) == NULL
)
765 sessions
= g_list_prepend(sessions
,
768 if (g_list_find_custom(participants
,
770 (GCompareFunc
)strcmp
) == NULL
)
771 participants
= g_list_prepend(participants
,
772 g_strdup(stream
->participant
));
775 /* Emit session acceptance */
776 for (; sessions
; sessions
=
777 g_list_delete_link(sessions
, sessions
)) {
778 PurpleMediaSession
*session
= sessions
->data
;
780 if (purple_media_accepted(media
, session
->id
, NULL
))
781 g_signal_emit(media
, purple_media_signals
[
783 PURPLE_MEDIA_INFO_ACCEPT
,
784 session
->id
, NULL
, local
);
787 /* Emit participant acceptance */
788 for (; participants
; participants
= g_list_delete_link(
789 participants
, participants
)) {
790 gchar
*participant
= participants
->data
;
792 if (purple_media_accepted(media
, NULL
, participant
))
793 g_signal_emit(media
, purple_media_signals
[
795 PURPLE_MEDIA_INFO_ACCEPT
,
796 NULL
, participant
, local
);
801 /* Emit conference acceptance */
802 if (purple_media_accepted(media
, NULL
, NULL
))
804 purple_media_signals
[STREAM_INFO
],
805 0, PURPLE_MEDIA_INFO_ACCEPT
,
809 } else if (type
== PURPLE_MEDIA_INFO_HANGUP
||
810 type
== PURPLE_MEDIA_INFO_REJECT
) {
813 g_return_if_fail(PURPLE_IS_MEDIA(media
));
815 streams
= purple_media_get_streams(media
,
816 session_id
, participant
);
818 /* Emit for stream */
819 for (; streams
; streams
=
820 g_list_delete_link(streams
, streams
)) {
821 PurpleMediaStream
*stream
= streams
->data
;
824 purple_media_signals
[STREAM_INFO
],
825 0, type
, stream
->session
->id
,
826 stream
->participant
, local
);
829 if (session_id
!= NULL
&& participant
!= NULL
) {
830 /* Everything that needs to be emitted has been */
831 } else if (session_id
== NULL
&& participant
== NULL
) {
832 /* Emit for everything in the conference */
833 GList
*sessions
= NULL
;
834 GList
*participants
= media
->priv
->participants
;
836 if (media
->priv
->sessions
!= NULL
)
837 sessions
= g_hash_table_get_values(
838 media
->priv
->sessions
);
840 /* Emit for sessions */
841 for (; sessions
; sessions
= g_list_delete_link(
842 sessions
, sessions
)) {
843 PurpleMediaSession
*session
= sessions
->data
;
845 g_signal_emit(media
, purple_media_signals
[
846 STREAM_INFO
], 0, type
,
847 session
->id
, NULL
, local
);
850 /* Emit for participants */
851 for (; participants
; participants
=
852 g_list_next(participants
)) {
853 gchar
*participant
= participants
->data
;
855 g_signal_emit(media
, purple_media_signals
[
856 STREAM_INFO
], 0, type
,
857 NULL
, participant
, local
);
860 /* Emit for conference */
862 purple_media_signals
[STREAM_INFO
],
863 0, type
, NULL
, NULL
, local
);
864 } else if (session_id
!= NULL
) {
865 /* Emit just the specific session */
866 PurpleMediaSession
*session
=
867 purple_media_get_session(
870 if (session
== NULL
) {
871 purple_debug_warning("media",
872 "Couldn't find session"
873 " to hangup/reject.\n");
875 g_signal_emit(media
, purple_media_signals
[
876 STREAM_INFO
], 0, type
,
877 session
->id
, NULL
, local
);
879 } else if (participant
!= NULL
) {
880 /* Emit just the specific participant */
881 if (!g_list_find_custom(media
->priv
->participants
,
882 participant
, (GCompareFunc
)strcmp
)) {
883 purple_debug_warning("media",
884 "Couldn't find participant"
885 " to hangup/reject.\n");
887 g_signal_emit(media
, purple_media_signals
[
888 STREAM_INFO
], 0, type
, NULL
,
893 purple_media_end(media
, session_id
, participant
);
897 g_signal_emit(media
, purple_media_signals
[STREAM_INFO
],
898 0, type
, session_id
, participant
, local
);
903 purple_media_set_params(PurpleMedia
*media
,
904 guint num_params
, GParameter
*params
)
907 g_return_if_fail(PURPLE_IS_MEDIA(media
));
909 purple_media_backend_set_params(media
->priv
->backend
, num_params
, params
);
914 purple_media_get_available_params(PurpleMedia
*media
)
916 static const gchar
*NULL_ARRAY
[] = { NULL
};
918 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL_ARRAY
);
920 return purple_media_backend_get_available_params(media
->priv
->backend
);
927 purple_media_param_is_supported(PurpleMedia
*media
, const gchar
*param
)
930 const gchar
**params
;
932 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
933 g_return_val_if_fail(param
!= NULL
, FALSE
);
935 params
= purple_media_backend_get_available_params(media
->priv
->backend
);
936 for (; *params
!= NULL
; ++params
)
937 if (!strcmp(*params
, param
))
945 purple_media_new_local_candidate_cb(PurpleMediaBackend
*backend
,
946 const gchar
*sess_id
, const gchar
*participant
,
947 PurpleMediaCandidate
*candidate
, PurpleMedia
*media
)
949 PurpleMediaSession
*session
=
950 purple_media_get_session(media
, sess_id
);
952 purple_media_insert_local_candidate(session
, participant
,
953 purple_media_candidate_copy(candidate
));
955 g_signal_emit(session
->media
, purple_media_signals
[NEW_CANDIDATE
],
956 0, session
->id
, participant
, candidate
);
960 purple_media_candidates_prepared_cb(PurpleMediaBackend
*backend
,
961 const gchar
*sess_id
, const gchar
*name
, PurpleMedia
*media
)
963 PurpleMediaStream
*stream_data
;
965 g_return_if_fail(PURPLE_IS_MEDIA(media
));
967 stream_data
= purple_media_get_stream(media
, sess_id
, name
);
968 stream_data
->candidates_prepared
= TRUE
;
970 g_signal_emit(media
, purple_media_signals
[CANDIDATES_PREPARED
],
974 /* callback called when a pair of transport candidates (local and remote)
975 * has been established */
977 purple_media_candidate_pair_established_cb(PurpleMediaBackend
*backend
,
978 const gchar
*sess_id
, const gchar
*name
,
979 PurpleMediaCandidate
*local_candidate
,
980 PurpleMediaCandidate
*remote_candidate
,
983 PurpleMediaStream
*stream
;
987 g_return_if_fail(PURPLE_IS_MEDIA(media
));
989 stream
= purple_media_get_stream(media
, sess_id
, name
);
990 id
= purple_media_candidate_get_component_id(local_candidate
);
992 iter
= stream
->active_local_candidates
;
993 for(; iter
; iter
= g_list_next(iter
)) {
994 PurpleMediaCandidate
*c
= iter
->data
;
995 if (id
== purple_media_candidate_get_component_id(c
)) {
997 stream
->active_local_candidates
=
998 g_list_delete_link(iter
, iter
);
999 stream
->active_local_candidates
= g_list_prepend(
1000 stream
->active_local_candidates
,
1001 purple_media_candidate_copy(
1007 stream
->active_local_candidates
= g_list_prepend(
1008 stream
->active_local_candidates
,
1009 purple_media_candidate_copy(
1012 id
= purple_media_candidate_get_component_id(local_candidate
);
1014 iter
= stream
->active_remote_candidates
;
1015 for(; iter
; iter
= g_list_next(iter
)) {
1016 PurpleMediaCandidate
*c
= iter
->data
;
1017 if (id
== purple_media_candidate_get_component_id(c
)) {
1019 stream
->active_remote_candidates
=
1020 g_list_delete_link(iter
, iter
);
1021 stream
->active_remote_candidates
= g_list_prepend(
1022 stream
->active_remote_candidates
,
1023 purple_media_candidate_copy(
1029 stream
->active_remote_candidates
= g_list_prepend(
1030 stream
->active_remote_candidates
,
1031 purple_media_candidate_copy(
1034 g_signal_emit(media
, purple_media_signals
[CANDIDATE_PAIR_ESTABLISHED
],
1035 0, sess_id
, name
, local_candidate
, remote_candidate
);
1036 purple_debug_info("media", "candidate pair established\n");
1040 purple_media_codecs_changed_cb(PurpleMediaBackend
*backend
,
1041 const gchar
*sess_id
, PurpleMedia
*media
)
1043 g_signal_emit(media
, purple_media_signals
[CODECS_CHANGED
], 0, sess_id
);
1048 purple_media_add_stream(PurpleMedia
*media
, const gchar
*sess_id
,
1049 const gchar
*who
, PurpleMediaSessionType type
,
1050 gboolean initiator
, const gchar
*transmitter
,
1051 guint num_params
, GParameter
*params
)
1054 PurpleMediaSession
*session
;
1056 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1058 if (!purple_media_backend_add_stream(media
->priv
->backend
,
1059 sess_id
, who
, type
, initiator
, transmitter
,
1060 num_params
, params
)) {
1061 purple_debug_error("media", "Error adding stream.\n");
1065 session
= purple_media_get_session(media
, sess_id
);
1068 session
= g_new0(PurpleMediaSession
, 1);
1069 session
->id
= g_strdup(sess_id
);
1070 session
->media
= media
;
1071 session
->type
= type
;
1072 session
->initiator
= initiator
;
1074 purple_media_add_session(media
, session
);
1075 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
1076 0, PURPLE_MEDIA_STATE_NEW
,
1080 if (!g_list_find_custom(media
->priv
->participants
,
1081 who
, (GCompareFunc
)strcmp
)) {
1082 media
->priv
->participants
= g_list_prepend(
1083 media
->priv
->participants
, g_strdup(who
));
1085 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
], 0,
1086 PURPLE_MEDIA_STATE_NEW
, NULL
, who
);
1089 if (purple_media_get_stream(media
, sess_id
, who
) == NULL
) {
1090 purple_media_insert_stream(session
, who
, initiator
);
1092 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
1093 0, PURPLE_MEDIA_STATE_NEW
,
1103 PurpleMediaManager
*
1104 purple_media_get_manager(PurpleMedia
*media
)
1106 PurpleMediaManager
*ret
;
1107 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1108 g_object_get(media
, "manager", &ret
, NULL
);
1112 PurpleMediaSessionType
1113 purple_media_get_session_type(PurpleMedia
*media
, const gchar
*sess_id
)
1116 PurpleMediaSession
*session
;
1117 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), PURPLE_MEDIA_NONE
);
1118 session
= purple_media_get_session(media
, sess_id
);
1119 return session
->type
;
1121 return PURPLE_MEDIA_NONE
;
1124 /* XXX: Should wait until codecs-ready is TRUE before using this function */
1126 purple_media_get_codecs(PurpleMedia
*media
, const gchar
*sess_id
)
1129 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1131 return purple_media_backend_get_codecs(media
->priv
->backend
, sess_id
);
1138 purple_media_get_local_candidates(PurpleMedia
*media
, const gchar
*sess_id
,
1139 const gchar
*participant
)
1142 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1144 return purple_media_backend_get_local_candidates(media
->priv
->backend
,
1145 sess_id
, participant
);
1152 purple_media_add_remote_candidates(PurpleMedia
*media
, const gchar
*sess_id
,
1153 const gchar
*participant
,
1154 GList
*remote_candidates
)
1157 PurpleMediaStream
*stream
;
1159 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1160 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1162 if (stream
== NULL
) {
1163 purple_debug_error("media",
1164 "purple_media_add_remote_candidates: "
1165 "couldn't find stream %s %s.\n",
1166 sess_id
? sess_id
: "(null)",
1167 participant
? participant
: "(null)");
1171 stream
->remote_candidates
= g_list_concat(stream
->remote_candidates
,
1172 purple_media_candidate_list_copy(remote_candidates
));
1174 purple_media_backend_add_remote_candidates(media
->priv
->backend
,
1175 sess_id
, participant
, remote_candidates
);
1180 purple_media_get_active_local_candidates(PurpleMedia
*media
,
1181 const gchar
*sess_id
, const gchar
*participant
)
1184 PurpleMediaStream
*stream
;
1185 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1186 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1187 return purple_media_candidate_list_copy(
1188 stream
->active_local_candidates
);
1195 purple_media_get_active_remote_candidates(PurpleMedia
*media
,
1196 const gchar
*sess_id
, const gchar
*participant
)
1199 PurpleMediaStream
*stream
;
1200 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1201 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1202 return purple_media_candidate_list_copy(
1203 stream
->active_remote_candidates
);
1210 purple_media_set_remote_codecs(PurpleMedia
*media
, const gchar
*sess_id
,
1211 const gchar
*participant
, GList
*codecs
)
1214 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1216 return purple_media_backend_set_remote_codecs(media
->priv
->backend
,
1217 sess_id
, participant
, codecs
);
1224 purple_media_candidates_prepared(PurpleMedia
*media
,
1225 const gchar
*session_id
, const gchar
*participant
)
1229 gboolean prepared
= TRUE
;
1231 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1233 streams
= purple_media_get_streams(media
, session_id
, participant
);
1235 for (; streams
; streams
= g_list_delete_link(streams
, streams
)) {
1236 PurpleMediaStream
*stream
= streams
->data
;
1237 if (stream
->candidates_prepared
== FALSE
) {
1238 g_list_free(streams
);
1251 purple_media_set_send_codec(PurpleMedia
*media
, const gchar
*sess_id
, PurpleMediaCodec
*codec
)
1254 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1256 return purple_media_backend_set_send_codec(
1257 media
->priv
->backend
, sess_id
, codec
);
1264 purple_media_set_encryption_parameters(PurpleMedia
*media
, const gchar
*sess_id
,
1265 const gchar
*cipher
, const gchar
*auth
,
1266 const gchar
*key
, gsize key_len
)
1269 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1270 return purple_media_backend_set_encryption_parameters(media
->priv
->backend
,
1271 sess_id
, cipher
, auth
, key
, key_len
);
1278 purple_media_set_decryption_parameters(PurpleMedia
*media
, const gchar
*sess_id
,
1279 const gchar
*participant
, const gchar
*cipher
,
1280 const gchar
*auth
, const gchar
*key
, gsize key_len
)
1283 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1284 return purple_media_backend_set_decryption_parameters(media
->priv
->backend
,
1285 sess_id
, participant
, cipher
, auth
, key
, key_len
);
1292 purple_media_codecs_ready(PurpleMedia
*media
, const gchar
*sess_id
)
1295 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1297 return purple_media_backend_codecs_ready(
1298 media
->priv
->backend
, sess_id
);
1305 purple_media_set_send_rtcp_mux(PurpleMedia
*media
, const gchar
*sess_id
,
1306 const gchar
*participant
, gboolean send_rtcp_mux
)
1309 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1311 return purple_media_backend_set_send_rtcp_mux(media
->priv
->backend
,
1312 sess_id
, participant
, send_rtcp_mux
);
1319 purple_media_is_initiator(PurpleMedia
*media
,
1320 const gchar
*sess_id
, const gchar
*participant
)
1323 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1325 if (sess_id
== NULL
&& participant
== NULL
)
1326 return media
->priv
->initiator
;
1327 else if (sess_id
!= NULL
&& participant
== NULL
) {
1328 PurpleMediaSession
*session
=
1329 purple_media_get_session(media
, sess_id
);
1330 return session
!= NULL
? session
->initiator
: FALSE
;
1331 } else if (sess_id
!= NULL
&& participant
!= NULL
) {
1332 PurpleMediaStream
*stream
= purple_media_get_stream(
1333 media
, sess_id
, participant
);
1334 return stream
!= NULL
? stream
->initiator
: FALSE
;
1341 purple_media_accepted(PurpleMedia
*media
, const gchar
*sess_id
,
1342 const gchar
*participant
)
1345 gboolean accepted
= TRUE
;
1347 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1349 if (sess_id
== NULL
&& participant
== NULL
) {
1350 GList
*streams
= media
->priv
->streams
;
1352 for (; streams
; streams
= g_list_next(streams
)) {
1353 PurpleMediaStream
*stream
= streams
->data
;
1354 if (stream
->accepted
== FALSE
) {
1359 } else if (sess_id
!= NULL
&& participant
== NULL
) {
1360 GList
*streams
= purple_media_get_streams(
1361 media
, sess_id
, NULL
);
1362 for (; streams
; streams
=
1363 g_list_delete_link(streams
, streams
)) {
1364 PurpleMediaStream
*stream
= streams
->data
;
1365 if (stream
->accepted
== FALSE
) {
1366 g_list_free(streams
);
1371 } else if (sess_id
!= NULL
&& participant
!= NULL
) {
1372 PurpleMediaStream
*stream
= purple_media_get_stream(
1373 media
, sess_id
, participant
);
1374 if (stream
== NULL
|| stream
->accepted
== FALSE
)
1384 void purple_media_set_input_volume(PurpleMedia
*media
,
1385 const gchar
*session_id
, double level
)
1388 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1389 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
));
1391 purple_media_backend_fs2_set_input_volume(
1392 PURPLE_MEDIA_BACKEND_FS2(
1393 media
->priv
->backend
),
1398 void purple_media_set_output_volume(PurpleMedia
*media
,
1399 const gchar
*session_id
, const gchar
*participant
,
1403 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1404 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
));
1406 purple_media_backend_fs2_set_output_volume(
1407 PURPLE_MEDIA_BACKEND_FS2(
1408 media
->priv
->backend
),
1409 session_id
, participant
, level
);
1414 purple_media_set_output_window(PurpleMedia
*media
, const gchar
*session_id
,
1415 const gchar
*participant
, gulong window_id
)
1418 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1420 return purple_media_manager_set_output_window(media
->priv
->manager
,
1421 media
, session_id
, participant
, window_id
);
1428 purple_media_remove_output_windows(PurpleMedia
*media
)
1431 GList
*iter
= media
->priv
->streams
;
1432 for (; iter
; iter
= g_list_next(iter
)) {
1433 PurpleMediaStream
*stream
= iter
->data
;
1434 purple_media_manager_remove_output_windows(
1435 media
->priv
->manager
, media
,
1436 stream
->session
->id
, stream
->participant
);
1439 iter
= purple_media_get_session_ids(media
);
1440 for (; iter
; iter
= g_list_delete_link(iter
, iter
)) {
1441 gchar
*session_name
= iter
->data
;
1442 purple_media_manager_remove_output_windows(
1443 media
->priv
->manager
, media
,
1444 session_name
, NULL
);
1451 purple_media_get_tee(PurpleMedia
*media
,
1452 const gchar
*session_id
, const gchar
*participant
)
1454 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1456 if (PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
))
1457 return purple_media_backend_fs2_get_tee(
1458 PURPLE_MEDIA_BACKEND_FS2(
1459 media
->priv
->backend
),
1460 session_id
, participant
);
1461 g_return_val_if_reached(NULL
);
1466 purple_media_send_dtmf(PurpleMedia
*media
, const gchar
*session_id
,
1467 gchar dtmf
, guint8 volume
, guint16 duration
)
1470 PurpleAccount
*account
= NULL
;
1471 PurpleConnection
*gc
= NULL
;
1472 PurpleProtocol
*protocol
= NULL
;
1473 PurpleMediaBackendIface
*backend_iface
= NULL
;
1477 account
= purple_media_get_account(media
);
1478 backend_iface
= PURPLE_MEDIA_BACKEND_GET_INTERFACE(media
->priv
->backend
);
1481 gc
= purple_account_get_connection(account
);
1483 protocol
= purple_connection_get_protocol(gc
);
1487 else if (dtmf
== 'b')
1489 else if (dtmf
== 'c')
1491 else if (dtmf
== 'd')
1494 g_return_val_if_fail(strchr("0123456789ABCD#*", dtmf
), FALSE
);
1496 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol
, MEDIA_IFACE
, send_dtmf
)
1497 && purple_protocol_media_iface_send_dtmf(protocol
, media
, dtmf
, volume
, duration
))
1500 } else if (backend_iface
&& backend_iface
->send_dtmf
1501 && backend_iface
->send_dtmf(media
->priv
->backend
,
1502 session_id
, dtmf
, volume
, duration
))