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 #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate))
87 static void purple_media_class_init (PurpleMediaClass
*klass
);
88 static void purple_media_init (PurpleMedia
*media
);
89 static void purple_media_dispose (GObject
*object
);
90 static void purple_media_finalize (GObject
*object
);
91 static void purple_media_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
);
92 static void purple_media_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
);
94 static void purple_media_new_local_candidate_cb(PurpleMediaBackend
*backend
,
95 const gchar
*sess_id
, const gchar
*participant
,
96 PurpleMediaCandidate
*candidate
, PurpleMedia
*media
);
97 static void purple_media_candidates_prepared_cb(PurpleMediaBackend
*backend
,
98 const gchar
*sess_id
, const gchar
*name
, PurpleMedia
*media
);
99 static void purple_media_candidate_pair_established_cb(
100 PurpleMediaBackend
*backend
,
101 const gchar
*sess_id
, const gchar
*name
,
102 PurpleMediaCandidate
*local_candidate
,
103 PurpleMediaCandidate
*remote_candidate
,
105 static void purple_media_codecs_changed_cb(PurpleMediaBackend
*backend
,
106 const gchar
*sess_id
, PurpleMedia
*media
);
108 static GObjectClass
*parent_class
= NULL
;
120 CANDIDATE_PAIR_ESTABLISHED
,
123 static guint purple_media_signals
[LAST_SIGNAL
] = {0};
130 PROP_CONFERENCE_TYPE
,
138 purple_media_get_type()
141 static GType type
= 0;
144 static const GTypeInfo info
= {
145 sizeof(PurpleMediaClass
),
148 (GClassInitFunc
) purple_media_class_init
,
153 (GInstanceInitFunc
) purple_media_init
,
156 type
= g_type_register_static(G_TYPE_OBJECT
, "PurpleMedia", &info
, 0);
166 purple_media_class_init (PurpleMediaClass
*klass
)
168 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
169 parent_class
= g_type_class_peek_parent(klass
);
171 gobject_class
->dispose
= purple_media_dispose
;
172 gobject_class
->finalize
= purple_media_finalize
;
173 gobject_class
->set_property
= purple_media_set_property
;
174 gobject_class
->get_property
= purple_media_get_property
;
176 g_object_class_install_property(gobject_class
, PROP_MANAGER
,
177 g_param_spec_object("manager",
178 "Purple Media Manager",
179 "The media manager that contains this media session.",
180 PURPLE_TYPE_MEDIA_MANAGER
,
181 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
182 G_PARAM_STATIC_STRINGS
));
185 * This one should be PURPLE_TYPE_MEDIA_BACKEND, but it doesn't
186 * like interfaces because they "aren't GObjects"
188 g_object_class_install_property(gobject_class
, PROP_BACKEND
,
189 g_param_spec_object("backend",
190 "Purple Media Backend",
191 "The backend object this media object uses.",
193 G_PARAM_READABLE
| G_PARAM_STATIC_STRINGS
));
195 g_object_class_install_property(gobject_class
, PROP_ACCOUNT
,
196 g_param_spec_object("account", "PurpleAccount",
197 "The account this media session is on.",
199 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
200 G_PARAM_STATIC_STRINGS
));
202 g_object_class_install_property(gobject_class
, PROP_CONFERENCE_TYPE
,
203 g_param_spec_string("conference-type",
205 "The type of conference that this media object "
206 "has been created to provide.",
208 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
209 G_PARAM_STATIC_STRINGS
));
211 g_object_class_install_property(gobject_class
, PROP_INITIATOR
,
212 g_param_spec_boolean("initiator",
214 "If the local user initiated the conference.",
216 G_PARAM_CONSTRUCT_ONLY
| G_PARAM_READWRITE
|
217 G_PARAM_STATIC_STRINGS
));
219 g_object_class_install_property(gobject_class
, PROP_PROTOCOL_DATA
,
220 g_param_spec_pointer("protocol-data",
222 "Data the protocol set on the media session.",
223 G_PARAM_READWRITE
| G_PARAM_STATIC_STRINGS
));
225 purple_media_signals
[S_ERROR
] = g_signal_new("error", G_TYPE_FROM_CLASS(klass
),
226 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
227 G_TYPE_NONE
, 1, G_TYPE_STRING
);
228 purple_media_signals
[CANDIDATES_PREPARED
] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass
),
229 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
230 G_TYPE_NONE
, 2, G_TYPE_STRING
,
232 purple_media_signals
[CODECS_CHANGED
] = g_signal_new("codecs-changed", G_TYPE_FROM_CLASS(klass
),
233 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
234 G_TYPE_NONE
, 1, G_TYPE_STRING
);
235 purple_media_signals
[LEVEL
] = g_signal_new("level", G_TYPE_FROM_CLASS(klass
),
236 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
237 G_TYPE_NONE
, 3, G_TYPE_STRING
,
238 G_TYPE_STRING
, G_TYPE_DOUBLE
);
239 purple_media_signals
[NEW_CANDIDATE
] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass
),
240 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
241 G_TYPE_NONE
, 3, G_TYPE_POINTER
,
242 G_TYPE_POINTER
, PURPLE_TYPE_MEDIA_CANDIDATE
);
243 purple_media_signals
[STATE_CHANGED
] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass
),
244 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
245 G_TYPE_NONE
, 3, PURPLE_MEDIA_TYPE_STATE
,
246 G_TYPE_STRING
, G_TYPE_STRING
);
247 purple_media_signals
[STREAM_INFO
] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass
),
248 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
249 G_TYPE_NONE
, 4, PURPLE_MEDIA_TYPE_INFO_TYPE
,
250 G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_BOOLEAN
);
251 purple_media_signals
[CANDIDATE_PAIR_ESTABLISHED
] = g_signal_new("candidate-pair-established", G_TYPE_FROM_CLASS(klass
),
252 G_SIGNAL_RUN_LAST
, 0, NULL
, NULL
, NULL
,
253 G_TYPE_NONE
, 4, G_TYPE_POINTER
, G_TYPE_POINTER
,
254 PURPLE_TYPE_MEDIA_CANDIDATE
, PURPLE_TYPE_MEDIA_CANDIDATE
);
255 g_type_class_add_private(klass
, sizeof(PurpleMediaPrivate
));
260 purple_media_init (PurpleMedia
*media
)
262 media
->priv
= PURPLE_MEDIA_GET_PRIVATE(media
);
263 memset(media
->priv
, 0, sizeof(*media
->priv
));
267 purple_media_stream_free(PurpleMediaStream
*stream
)
272 g_free(stream
->participant
);
274 if (stream
->local_candidates
)
275 purple_media_candidate_list_free(stream
->local_candidates
);
276 if (stream
->remote_candidates
)
277 purple_media_candidate_list_free(stream
->remote_candidates
);
279 if (stream
->active_local_candidates
)
280 purple_media_candidate_list_free(
281 stream
->active_local_candidates
);
282 if (stream
->active_remote_candidates
)
283 purple_media_candidate_list_free(
284 stream
->active_remote_candidates
);
290 purple_media_session_free(PurpleMediaSession
*session
)
300 purple_media_dispose(GObject
*media
)
302 PurpleMediaPrivate
*priv
= PURPLE_MEDIA_GET_PRIVATE(media
);
304 purple_debug_info("media","purple_media_dispose\n");
306 purple_media_manager_remove_media(priv
->manager
, PURPLE_MEDIA(media
));
309 g_object_unref(priv
->backend
);
310 priv
->backend
= NULL
;
314 g_object_unref(priv
->manager
);
315 priv
->manager
= NULL
;
318 G_OBJECT_CLASS(parent_class
)->dispose(media
);
322 purple_media_finalize(GObject
*media
)
324 PurpleMediaPrivate
*priv
= PURPLE_MEDIA_GET_PRIVATE(media
);
325 purple_debug_info("media","purple_media_finalize\n");
327 for (; priv
->streams
; priv
->streams
= g_list_delete_link(priv
->streams
, priv
->streams
))
328 purple_media_stream_free(priv
->streams
->data
);
330 for (; priv
->participants
; priv
->participants
= g_list_delete_link(
331 priv
->participants
, priv
->participants
))
332 g_free(priv
->participants
->data
);
334 if (priv
->sessions
) {
335 GList
*sessions
= g_hash_table_get_values(priv
->sessions
);
336 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
337 purple_media_session_free(sessions
->data
);
339 g_hash_table_destroy(priv
->sessions
);
342 G_OBJECT_CLASS(parent_class
)->finalize(media
);
346 purple_media_set_property (GObject
*object
, guint prop_id
, const GValue
*value
, GParamSpec
*pspec
)
349 g_return_if_fail(PURPLE_IS_MEDIA(object
));
351 media
= PURPLE_MEDIA(object
);
355 media
->priv
->manager
= g_value_dup_object(value
);
358 media
->priv
->account
= g_value_get_object(value
);
360 case PROP_CONFERENCE_TYPE
:
361 media
->priv
->conference_type
=
362 g_value_dup_string(value
);
363 media
->priv
->backend
= g_object_new(
364 purple_media_manager_get_backend_type(
365 purple_media_manager_get()),
367 media
->priv
->conference_type
,
370 g_signal_connect(media
->priv
->backend
,
371 "active-candidate-pair",
373 purple_media_candidate_pair_established_cb
),
375 g_signal_connect(media
->priv
->backend
,
376 "candidates-prepared",
378 purple_media_candidates_prepared_cb
),
380 g_signal_connect(media
->priv
->backend
,
383 purple_media_codecs_changed_cb
),
385 g_signal_connect(media
->priv
->backend
,
388 purple_media_new_local_candidate_cb
),
392 media
->priv
->initiator
= g_value_get_boolean(value
);
394 case PROP_PROTOCOL_DATA
:
395 media
->priv
->protocol_data
= g_value_get_pointer(value
);
398 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
404 purple_media_get_property (GObject
*object
, guint prop_id
, GValue
*value
, GParamSpec
*pspec
)
407 g_return_if_fail(PURPLE_IS_MEDIA(object
));
409 media
= PURPLE_MEDIA(object
);
413 g_value_set_object(value
, media
->priv
->manager
);
416 g_value_set_object(value
, media
->priv
->backend
);
419 g_value_set_object(value
, media
->priv
->account
);
421 case PROP_CONFERENCE_TYPE
:
422 g_value_set_string(value
,
423 media
->priv
->conference_type
);
426 g_value_set_boolean(value
, media
->priv
->initiator
);
428 case PROP_PROTOCOL_DATA
:
429 g_value_set_pointer(value
, media
->priv
->protocol_data
);
432 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
438 static PurpleMediaSession
*
439 purple_media_get_session(PurpleMedia
*media
, const gchar
*sess_id
)
441 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
442 return (PurpleMediaSession
*) (media
->priv
->sessions
) ?
443 g_hash_table_lookup(media
->priv
->sessions
, sess_id
) : NULL
;
446 static PurpleMediaStream
*
447 purple_media_get_stream(PurpleMedia
*media
, const gchar
*session
, const gchar
*participant
)
451 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
453 streams
= media
->priv
->streams
;
455 for (; streams
; streams
= g_list_next(streams
)) {
456 PurpleMediaStream
*stream
= streams
->data
;
457 if (purple_strequal(stream
->session
->id
, session
) &&
458 purple_strequal(stream
->participant
, participant
))
466 purple_media_get_streams(PurpleMedia
*media
, const gchar
*session
,
467 const gchar
*participant
)
472 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
474 streams
= media
->priv
->streams
;
476 for (; streams
; streams
= g_list_next(streams
)) {
477 PurpleMediaStream
*stream
= streams
->data
;
478 if ((session
== NULL
||
479 purple_strequal(stream
->session
->id
, session
)) &&
480 (participant
== NULL
||
481 purple_strequal(stream
->participant
, participant
)))
482 ret
= g_list_append(ret
, stream
);
489 purple_media_add_session(PurpleMedia
*media
, PurpleMediaSession
*session
)
491 g_return_if_fail(PURPLE_IS_MEDIA(media
));
492 g_return_if_fail(session
!= NULL
);
494 if (!media
->priv
->sessions
) {
495 purple_debug_info("media", "Creating hash table for sessions\n");
496 media
->priv
->sessions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
499 g_hash_table_insert(media
->priv
->sessions
, g_strdup(session
->id
), session
);
504 purple_media_remove_session(PurpleMedia
*media
, PurpleMediaSession
*session
)
506 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
507 return g_hash_table_remove(media
->priv
->sessions
, session
->id
);
511 static PurpleMediaStream
*
512 purple_media_insert_stream(PurpleMediaSession
*session
,
513 const gchar
*name
, gboolean initiator
)
515 PurpleMediaStream
*media_stream
;
517 g_return_val_if_fail(session
!= NULL
, NULL
);
519 media_stream
= g_new0(PurpleMediaStream
, 1);
520 media_stream
->participant
= g_strdup(name
);
521 media_stream
->session
= session
;
522 media_stream
->initiator
= initiator
;
524 session
->media
->priv
->streams
=
525 g_list_append(session
->media
->priv
->streams
, media_stream
);
531 purple_media_insert_local_candidate(PurpleMediaSession
*session
, const gchar
*name
,
532 PurpleMediaCandidate
*candidate
)
534 PurpleMediaStream
*stream
;
536 g_return_if_fail(session
!= NULL
);
538 stream
= purple_media_get_stream(session
->media
, session
->id
, name
);
539 stream
->local_candidates
= g_list_append(stream
->local_candidates
, candidate
);
544 purple_media_get_session_ids(PurpleMedia
*media
)
547 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
548 return media
->priv
->sessions
!= NULL
?
549 g_hash_table_get_keys(media
->priv
->sessions
) : NULL
;
557 purple_media_get_src(PurpleMedia
*media
, const gchar
*sess_id
)
559 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
561 if (PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
))
562 return purple_media_backend_fs2_get_src(
563 PURPLE_MEDIA_BACKEND_FS2(
564 media
->priv
->backend
), sess_id
);
566 g_return_val_if_reached(NULL
);
571 purple_media_get_account(PurpleMedia
*media
)
574 PurpleAccount
*account
;
575 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
576 g_object_get(G_OBJECT(media
), "account", &account
, NULL
);
584 purple_media_get_protocol_data(PurpleMedia
*media
)
587 gpointer protocol_data
;
588 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
589 g_object_get(G_OBJECT(media
), "protocol-data", &protocol_data
, NULL
);
590 return protocol_data
;
597 purple_media_set_protocol_data(PurpleMedia
*media
, gpointer protocol_data
)
600 g_return_if_fail(PURPLE_IS_MEDIA(media
));
601 g_object_set(G_OBJECT(media
), "protocol-data", protocol_data
, NULL
);
606 purple_media_error(PurpleMedia
*media
, const gchar
*error
, ...)
612 g_return_if_fail(PURPLE_IS_MEDIA(media
));
614 va_start(args
, error
);
615 message
= g_strdup_vprintf(error
, args
);
618 purple_debug_error("media", "%s\n", message
);
619 g_signal_emit(media
, purple_media_signals
[S_ERROR
], 0, message
);
626 purple_media_end(PurpleMedia
*media
,
627 const gchar
*session_id
, const gchar
*participant
)
630 GList
*iter
, *sessions
= NULL
, *participants
= NULL
;
632 g_return_if_fail(PURPLE_IS_MEDIA(media
));
634 iter
= purple_media_get_streams(media
, session_id
, participant
);
636 /* Free matching streams */
637 for (; iter
; iter
= g_list_delete_link(iter
, iter
)) {
638 PurpleMediaStream
*stream
= iter
->data
;
640 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
641 0, PURPLE_MEDIA_STATE_END
,
642 stream
->session
->id
, stream
->participant
);
644 media
->priv
->streams
=
645 g_list_remove(media
->priv
->streams
, stream
);
647 if (g_list_find(sessions
, stream
->session
) == NULL
)
648 sessions
= g_list_prepend(sessions
, stream
->session
);
650 if (g_list_find_custom(participants
, stream
->participant
,
651 (GCompareFunc
)strcmp
) == NULL
)
652 participants
= g_list_prepend(participants
,
653 g_strdup(stream
->participant
));
655 purple_media_stream_free(stream
);
658 iter
= media
->priv
->streams
;
660 /* Reduce to list of sessions to remove */
661 for (; iter
; iter
= g_list_next(iter
)) {
662 PurpleMediaStream
*stream
= iter
->data
;
664 sessions
= g_list_remove(sessions
, stream
->session
);
667 /* Free sessions with no streams left */
668 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
669 PurpleMediaSession
*session
= sessions
->data
;
671 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
672 0, PURPLE_MEDIA_STATE_END
,
675 g_hash_table_remove(media
->priv
->sessions
, session
->id
);
676 purple_media_session_free(session
);
679 iter
= media
->priv
->streams
;
681 /* Reduce to list of participants to remove */
682 for (; iter
; iter
= g_list_next(iter
)) {
683 PurpleMediaStream
*stream
= iter
->data
;
686 tmp
= g_list_find_custom(participants
,
687 stream
->participant
, (GCompareFunc
)strcmp
);
691 participants
= g_list_delete_link(participants
, tmp
);
695 /* Remove participants with no streams left (just emit the signal) */
696 for (; participants
; participants
=
697 g_list_delete_link(participants
, participants
)) {
698 gchar
*participant
= participants
->data
;
699 GList
*link
= g_list_find_custom(media
->priv
->participants
,
700 participant
, (GCompareFunc
)strcmp
);
702 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
703 0, PURPLE_MEDIA_STATE_END
,
708 media
->priv
->participants
= g_list_delete_link(
709 media
->priv
->participants
, link
);
715 /* Free the conference if no sessions left */
716 if (media
->priv
->sessions
!= NULL
&&
717 g_hash_table_size(media
->priv
->sessions
) == 0) {
718 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
719 0, PURPLE_MEDIA_STATE_END
,
721 g_object_unref(media
);
728 purple_media_stream_info(PurpleMedia
*media
, PurpleMediaInfoType type
,
729 const gchar
*session_id
, const gchar
*participant
,
733 g_return_if_fail(PURPLE_IS_MEDIA(media
));
735 if (type
== PURPLE_MEDIA_INFO_ACCEPT
) {
736 GList
*streams
, *sessions
= NULL
, *participants
= NULL
;
738 g_return_if_fail(PURPLE_IS_MEDIA(media
));
740 streams
= purple_media_get_streams(media
,
741 session_id
, participant
);
743 /* Emit stream acceptance */
744 for (; streams
; streams
=
745 g_list_delete_link(streams
, streams
)) {
746 PurpleMediaStream
*stream
= streams
->data
;
748 stream
->accepted
= TRUE
;
751 purple_media_signals
[STREAM_INFO
],
752 0, type
, stream
->session
->id
,
753 stream
->participant
, local
);
755 if (g_list_find(sessions
, stream
->session
) == NULL
)
756 sessions
= g_list_prepend(sessions
,
759 if (g_list_find_custom(participants
,
761 (GCompareFunc
)strcmp
) == NULL
)
762 participants
= g_list_prepend(participants
,
763 g_strdup(stream
->participant
));
766 /* Emit session acceptance */
767 for (; sessions
; sessions
=
768 g_list_delete_link(sessions
, sessions
)) {
769 PurpleMediaSession
*session
= sessions
->data
;
771 if (purple_media_accepted(media
, session
->id
, NULL
))
772 g_signal_emit(media
, purple_media_signals
[
774 PURPLE_MEDIA_INFO_ACCEPT
,
775 session
->id
, NULL
, local
);
778 /* Emit participant acceptance */
779 for (; participants
; participants
= g_list_delete_link(
780 participants
, participants
)) {
781 gchar
*participant
= participants
->data
;
783 if (purple_media_accepted(media
, NULL
, participant
))
784 g_signal_emit(media
, purple_media_signals
[
786 PURPLE_MEDIA_INFO_ACCEPT
,
787 NULL
, participant
, local
);
792 /* Emit conference acceptance */
793 if (purple_media_accepted(media
, NULL
, NULL
))
795 purple_media_signals
[STREAM_INFO
],
796 0, PURPLE_MEDIA_INFO_ACCEPT
,
800 } else if (type
== PURPLE_MEDIA_INFO_HANGUP
||
801 type
== PURPLE_MEDIA_INFO_REJECT
) {
804 g_return_if_fail(PURPLE_IS_MEDIA(media
));
806 streams
= purple_media_get_streams(media
,
807 session_id
, participant
);
809 /* Emit for stream */
810 for (; streams
; streams
=
811 g_list_delete_link(streams
, streams
)) {
812 PurpleMediaStream
*stream
= streams
->data
;
815 purple_media_signals
[STREAM_INFO
],
816 0, type
, stream
->session
->id
,
817 stream
->participant
, local
);
820 if (session_id
!= NULL
&& participant
!= NULL
) {
821 /* Everything that needs to be emitted has been */
822 } else if (session_id
== NULL
&& participant
== NULL
) {
823 /* Emit for everything in the conference */
824 GList
*sessions
= NULL
;
825 GList
*participants
= media
->priv
->participants
;
827 if (media
->priv
->sessions
!= NULL
)
828 sessions
= g_hash_table_get_values(
829 media
->priv
->sessions
);
831 /* Emit for sessions */
832 for (; sessions
; sessions
= g_list_delete_link(
833 sessions
, sessions
)) {
834 PurpleMediaSession
*session
= sessions
->data
;
836 g_signal_emit(media
, purple_media_signals
[
837 STREAM_INFO
], 0, type
,
838 session
->id
, NULL
, local
);
841 /* Emit for participants */
842 for (; participants
; participants
=
843 g_list_next(participants
)) {
844 gchar
*participant
= participants
->data
;
846 g_signal_emit(media
, purple_media_signals
[
847 STREAM_INFO
], 0, type
,
848 NULL
, participant
, local
);
851 /* Emit for conference */
853 purple_media_signals
[STREAM_INFO
],
854 0, type
, NULL
, NULL
, local
);
855 } else if (session_id
!= NULL
) {
856 /* Emit just the specific session */
857 PurpleMediaSession
*session
=
858 purple_media_get_session(
861 if (session
== NULL
) {
862 purple_debug_warning("media",
863 "Couldn't find session"
864 " to hangup/reject.\n");
866 g_signal_emit(media
, purple_media_signals
[
867 STREAM_INFO
], 0, type
,
868 session
->id
, NULL
, local
);
870 } else if (participant
!= NULL
) {
871 /* Emit just the specific participant */
872 if (!g_list_find_custom(media
->priv
->participants
,
873 participant
, (GCompareFunc
)strcmp
)) {
874 purple_debug_warning("media",
875 "Couldn't find participant"
876 " to hangup/reject.\n");
878 g_signal_emit(media
, purple_media_signals
[
879 STREAM_INFO
], 0, type
, NULL
,
884 purple_media_end(media
, session_id
, participant
);
888 g_signal_emit(media
, purple_media_signals
[STREAM_INFO
],
889 0, type
, session_id
, participant
, local
);
894 purple_media_set_params(PurpleMedia
*media
,
895 guint num_params
, GParameter
*params
)
898 g_return_if_fail(PURPLE_IS_MEDIA(media
));
900 purple_media_backend_set_params(media
->priv
->backend
, num_params
, params
);
905 purple_media_get_available_params(PurpleMedia
*media
)
907 static const gchar
*NULL_ARRAY
[] = { NULL
};
909 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL_ARRAY
);
911 return purple_media_backend_get_available_params(media
->priv
->backend
);
918 purple_media_param_is_supported(PurpleMedia
*media
, const gchar
*param
)
921 const gchar
**params
;
923 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
924 g_return_val_if_fail(param
!= NULL
, FALSE
);
926 params
= purple_media_backend_get_available_params(media
->priv
->backend
);
927 for (; *params
!= NULL
; ++params
)
928 if (purple_strequal(*params
, param
))
936 purple_media_new_local_candidate_cb(PurpleMediaBackend
*backend
,
937 const gchar
*sess_id
, const gchar
*participant
,
938 PurpleMediaCandidate
*candidate
, PurpleMedia
*media
)
940 PurpleMediaSession
*session
=
941 purple_media_get_session(media
, sess_id
);
943 purple_media_insert_local_candidate(session
, participant
,
944 purple_media_candidate_copy(candidate
));
946 g_signal_emit(session
->media
, purple_media_signals
[NEW_CANDIDATE
],
947 0, session
->id
, participant
, candidate
);
951 purple_media_candidates_prepared_cb(PurpleMediaBackend
*backend
,
952 const gchar
*sess_id
, const gchar
*name
, PurpleMedia
*media
)
954 PurpleMediaStream
*stream_data
;
956 g_return_if_fail(PURPLE_IS_MEDIA(media
));
958 stream_data
= purple_media_get_stream(media
, sess_id
, name
);
959 stream_data
->candidates_prepared
= TRUE
;
961 g_signal_emit(media
, purple_media_signals
[CANDIDATES_PREPARED
],
965 /* callback called when a pair of transport candidates (local and remote)
966 * has been established */
968 purple_media_candidate_pair_established_cb(PurpleMediaBackend
*backend
,
969 const gchar
*sess_id
, const gchar
*name
,
970 PurpleMediaCandidate
*local_candidate
,
971 PurpleMediaCandidate
*remote_candidate
,
974 PurpleMediaStream
*stream
;
978 g_return_if_fail(PURPLE_IS_MEDIA(media
));
980 stream
= purple_media_get_stream(media
, sess_id
, name
);
981 id
= purple_media_candidate_get_component_id(local_candidate
);
983 iter
= stream
->active_local_candidates
;
984 for(; iter
; iter
= g_list_next(iter
)) {
985 PurpleMediaCandidate
*c
= iter
->data
;
986 if (id
== purple_media_candidate_get_component_id(c
)) {
988 stream
->active_local_candidates
=
989 g_list_delete_link(iter
, iter
);
990 stream
->active_local_candidates
= g_list_prepend(
991 stream
->active_local_candidates
,
992 purple_media_candidate_copy(
998 stream
->active_local_candidates
= g_list_prepend(
999 stream
->active_local_candidates
,
1000 purple_media_candidate_copy(
1003 id
= purple_media_candidate_get_component_id(local_candidate
);
1005 iter
= stream
->active_remote_candidates
;
1006 for(; iter
; iter
= g_list_next(iter
)) {
1007 PurpleMediaCandidate
*c
= iter
->data
;
1008 if (id
== purple_media_candidate_get_component_id(c
)) {
1010 stream
->active_remote_candidates
=
1011 g_list_delete_link(iter
, iter
);
1012 stream
->active_remote_candidates
= g_list_prepend(
1013 stream
->active_remote_candidates
,
1014 purple_media_candidate_copy(
1020 stream
->active_remote_candidates
= g_list_prepend(
1021 stream
->active_remote_candidates
,
1022 purple_media_candidate_copy(
1025 g_signal_emit(media
, purple_media_signals
[CANDIDATE_PAIR_ESTABLISHED
],
1026 0, sess_id
, name
, local_candidate
, remote_candidate
);
1027 purple_debug_info("media", "candidate pair established\n");
1031 purple_media_codecs_changed_cb(PurpleMediaBackend
*backend
,
1032 const gchar
*sess_id
, PurpleMedia
*media
)
1034 g_signal_emit(media
, purple_media_signals
[CODECS_CHANGED
], 0, sess_id
);
1039 purple_media_add_stream(PurpleMedia
*media
, const gchar
*sess_id
,
1040 const gchar
*who
, PurpleMediaSessionType type
,
1041 gboolean initiator
, const gchar
*transmitter
,
1042 guint num_params
, GParameter
*params
)
1045 PurpleMediaSession
*session
;
1047 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1049 if (!purple_media_backend_add_stream(media
->priv
->backend
,
1050 sess_id
, who
, type
, initiator
, transmitter
,
1051 num_params
, params
)) {
1052 purple_debug_error("media", "Error adding stream.\n");
1056 session
= purple_media_get_session(media
, sess_id
);
1059 session
= g_new0(PurpleMediaSession
, 1);
1060 session
->id
= g_strdup(sess_id
);
1061 session
->media
= media
;
1062 session
->type
= type
;
1063 session
->initiator
= initiator
;
1065 purple_media_add_session(media
, session
);
1066 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
1067 0, PURPLE_MEDIA_STATE_NEW
,
1071 if (!g_list_find_custom(media
->priv
->participants
,
1072 who
, (GCompareFunc
)strcmp
)) {
1073 media
->priv
->participants
= g_list_prepend(
1074 media
->priv
->participants
, g_strdup(who
));
1076 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
], 0,
1077 PURPLE_MEDIA_STATE_NEW
, NULL
, who
);
1080 if (purple_media_get_stream(media
, sess_id
, who
) == NULL
) {
1081 purple_media_insert_stream(session
, who
, initiator
);
1083 g_signal_emit(media
, purple_media_signals
[STATE_CHANGED
],
1084 0, PURPLE_MEDIA_STATE_NEW
,
1094 PurpleMediaManager
*
1095 purple_media_get_manager(PurpleMedia
*media
)
1097 PurpleMediaManager
*ret
;
1098 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1099 g_object_get(media
, "manager", &ret
, NULL
);
1103 PurpleMediaSessionType
1104 purple_media_get_session_type(PurpleMedia
*media
, const gchar
*sess_id
)
1107 PurpleMediaSession
*session
;
1108 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), PURPLE_MEDIA_NONE
);
1109 session
= purple_media_get_session(media
, sess_id
);
1110 return session
->type
;
1112 return PURPLE_MEDIA_NONE
;
1115 /* XXX: Should wait until codecs-ready is TRUE before using this function */
1117 purple_media_get_codecs(PurpleMedia
*media
, const gchar
*sess_id
)
1120 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1122 return purple_media_backend_get_codecs(media
->priv
->backend
, sess_id
);
1129 purple_media_get_local_candidates(PurpleMedia
*media
, const gchar
*sess_id
,
1130 const gchar
*participant
)
1133 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1135 return purple_media_backend_get_local_candidates(media
->priv
->backend
,
1136 sess_id
, participant
);
1143 purple_media_add_remote_candidates(PurpleMedia
*media
, const gchar
*sess_id
,
1144 const gchar
*participant
,
1145 GList
*remote_candidates
)
1148 PurpleMediaStream
*stream
;
1150 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1151 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1153 if (stream
== NULL
) {
1154 purple_debug_error("media",
1155 "purple_media_add_remote_candidates: "
1156 "couldn't find stream %s %s.\n",
1157 sess_id
? sess_id
: "(null)",
1158 participant
? participant
: "(null)");
1162 stream
->remote_candidates
= g_list_concat(stream
->remote_candidates
,
1163 purple_media_candidate_list_copy(remote_candidates
));
1165 purple_media_backend_add_remote_candidates(media
->priv
->backend
,
1166 sess_id
, participant
, remote_candidates
);
1171 purple_media_get_active_local_candidates(PurpleMedia
*media
,
1172 const gchar
*sess_id
, const gchar
*participant
)
1175 PurpleMediaStream
*stream
;
1176 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1177 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1178 return purple_media_candidate_list_copy(
1179 stream
->active_local_candidates
);
1186 purple_media_get_active_remote_candidates(PurpleMedia
*media
,
1187 const gchar
*sess_id
, const gchar
*participant
)
1190 PurpleMediaStream
*stream
;
1191 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1192 stream
= purple_media_get_stream(media
, sess_id
, participant
);
1193 return purple_media_candidate_list_copy(
1194 stream
->active_remote_candidates
);
1201 purple_media_set_remote_codecs(PurpleMedia
*media
, const gchar
*sess_id
,
1202 const gchar
*participant
, GList
*codecs
)
1205 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1207 return purple_media_backend_set_remote_codecs(media
->priv
->backend
,
1208 sess_id
, participant
, codecs
);
1215 purple_media_candidates_prepared(PurpleMedia
*media
,
1216 const gchar
*session_id
, const gchar
*participant
)
1220 gboolean prepared
= TRUE
;
1222 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1224 streams
= purple_media_get_streams(media
, session_id
, participant
);
1226 for (; streams
; streams
= g_list_delete_link(streams
, streams
)) {
1227 PurpleMediaStream
*stream
= streams
->data
;
1228 if (stream
->candidates_prepared
== FALSE
) {
1229 g_list_free(streams
);
1242 purple_media_set_send_codec(PurpleMedia
*media
, const gchar
*sess_id
, PurpleMediaCodec
*codec
)
1245 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1247 return purple_media_backend_set_send_codec(
1248 media
->priv
->backend
, sess_id
, codec
);
1255 purple_media_set_encryption_parameters(PurpleMedia
*media
, const gchar
*sess_id
,
1256 const gchar
*cipher
, const gchar
*auth
,
1257 const gchar
*key
, gsize key_len
)
1260 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1261 return purple_media_backend_set_encryption_parameters(media
->priv
->backend
,
1262 sess_id
, cipher
, auth
, key
, key_len
);
1269 purple_media_set_decryption_parameters(PurpleMedia
*media
, const gchar
*sess_id
,
1270 const gchar
*participant
, const gchar
*cipher
,
1271 const gchar
*auth
, const gchar
*key
, gsize key_len
)
1274 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1275 return purple_media_backend_set_decryption_parameters(media
->priv
->backend
,
1276 sess_id
, participant
, cipher
, auth
, key
, key_len
);
1283 purple_media_codecs_ready(PurpleMedia
*media
, const gchar
*sess_id
)
1286 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1288 return purple_media_backend_codecs_ready(
1289 media
->priv
->backend
, sess_id
);
1296 purple_media_set_send_rtcp_mux(PurpleMedia
*media
, const gchar
*sess_id
,
1297 const gchar
*participant
, gboolean send_rtcp_mux
)
1300 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1302 return purple_media_backend_set_send_rtcp_mux(media
->priv
->backend
,
1303 sess_id
, participant
, send_rtcp_mux
);
1310 purple_media_is_initiator(PurpleMedia
*media
,
1311 const gchar
*sess_id
, const gchar
*participant
)
1314 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1316 if (sess_id
== NULL
&& participant
== NULL
)
1317 return media
->priv
->initiator
;
1318 else if (sess_id
!= NULL
&& participant
== NULL
) {
1319 PurpleMediaSession
*session
=
1320 purple_media_get_session(media
, sess_id
);
1321 return session
!= NULL
? session
->initiator
: FALSE
;
1322 } else if (sess_id
!= NULL
&& participant
!= NULL
) {
1323 PurpleMediaStream
*stream
= purple_media_get_stream(
1324 media
, sess_id
, participant
);
1325 return stream
!= NULL
? stream
->initiator
: FALSE
;
1332 purple_media_accepted(PurpleMedia
*media
, const gchar
*sess_id
,
1333 const gchar
*participant
)
1336 gboolean accepted
= TRUE
;
1338 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1340 if (sess_id
== NULL
&& participant
== NULL
) {
1341 GList
*streams
= media
->priv
->streams
;
1343 for (; streams
; streams
= g_list_next(streams
)) {
1344 PurpleMediaStream
*stream
= streams
->data
;
1345 if (stream
->accepted
== FALSE
) {
1350 } else if (sess_id
!= NULL
&& participant
== NULL
) {
1351 GList
*streams
= purple_media_get_streams(
1352 media
, sess_id
, NULL
);
1353 for (; streams
; streams
=
1354 g_list_delete_link(streams
, streams
)) {
1355 PurpleMediaStream
*stream
= streams
->data
;
1356 if (stream
->accepted
== FALSE
) {
1357 g_list_free(streams
);
1362 } else if (sess_id
!= NULL
&& participant
!= NULL
) {
1363 PurpleMediaStream
*stream
= purple_media_get_stream(
1364 media
, sess_id
, participant
);
1365 if (stream
== NULL
|| stream
->accepted
== FALSE
)
1375 void purple_media_set_input_volume(PurpleMedia
*media
,
1376 const gchar
*session_id
, double level
)
1379 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1380 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
));
1382 purple_media_backend_fs2_set_input_volume(
1383 PURPLE_MEDIA_BACKEND_FS2(
1384 media
->priv
->backend
),
1389 void purple_media_set_output_volume(PurpleMedia
*media
,
1390 const gchar
*session_id
, const gchar
*participant
,
1394 g_return_if_fail(PURPLE_IS_MEDIA(media
));
1395 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
));
1397 purple_media_backend_fs2_set_output_volume(
1398 PURPLE_MEDIA_BACKEND_FS2(
1399 media
->priv
->backend
),
1400 session_id
, participant
, level
);
1405 purple_media_set_output_window(PurpleMedia
*media
, const gchar
*session_id
,
1406 const gchar
*participant
, gulong window_id
)
1409 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), FALSE
);
1411 return purple_media_manager_set_output_window(media
->priv
->manager
,
1412 media
, session_id
, participant
, window_id
);
1419 purple_media_remove_output_windows(PurpleMedia
*media
)
1422 GList
*iter
= media
->priv
->streams
;
1423 for (; iter
; iter
= g_list_next(iter
)) {
1424 PurpleMediaStream
*stream
= iter
->data
;
1425 purple_media_manager_remove_output_windows(
1426 media
->priv
->manager
, media
,
1427 stream
->session
->id
, stream
->participant
);
1430 iter
= purple_media_get_session_ids(media
);
1431 for (; iter
; iter
= g_list_delete_link(iter
, iter
)) {
1432 gchar
*session_name
= iter
->data
;
1433 purple_media_manager_remove_output_windows(
1434 media
->priv
->manager
, media
,
1435 session_name
, NULL
);
1442 purple_media_get_tee(PurpleMedia
*media
,
1443 const gchar
*session_id
, const gchar
*participant
)
1445 g_return_val_if_fail(PURPLE_IS_MEDIA(media
), NULL
);
1447 if (PURPLE_IS_MEDIA_BACKEND_FS2(media
->priv
->backend
))
1448 return purple_media_backend_fs2_get_tee(
1449 PURPLE_MEDIA_BACKEND_FS2(
1450 media
->priv
->backend
),
1451 session_id
, participant
);
1452 g_return_val_if_reached(NULL
);
1457 purple_media_send_dtmf(PurpleMedia
*media
, const gchar
*session_id
,
1458 gchar dtmf
, guint8 volume
, guint16 duration
)
1461 PurpleMediaBackendIface
*backend_iface
= NULL
;
1465 backend_iface
= PURPLE_MEDIA_BACKEND_GET_INTERFACE(media
->priv
->backend
);
1470 else if (dtmf
== 'b')
1472 else if (dtmf
== 'c')
1474 else if (dtmf
== 'd')
1477 g_return_val_if_fail(strchr("0123456789ABCD#*", dtmf
), FALSE
);
1479 if (backend_iface
&& backend_iface
->send_dtmf
1480 && backend_iface
->send_dtmf(media
->priv
->backend
,
1481 session_id
, dtmf
, volume
, duration
))