Merged pidgin/main into default
[pidgin-git.git] / libpurple / media.c
blobb0b5fdebb6d46ef708c3dcefaa1764515ae7b8a3
1 /* purple
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
5 * source distribution.
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
21 #include "internal.h"
23 #include "account.h"
24 #include "media.h"
25 #include "media/backend-iface.h"
26 #include "mediamanager.h"
28 #include "debug.h"
30 #ifdef USE_GSTREAMER
31 #include "media/backend-fs2.h"
32 #include "media-gst.h"
33 #endif /* USE_GSTREAMER */
35 #ifdef USE_VV
36 /** @copydoc _PurpleMediaSession */
37 typedef struct _PurpleMediaSession PurpleMediaSession;
38 /** @copydoc _PurpleMediaStream */
39 typedef struct _PurpleMediaStream PurpleMediaStream;
41 struct _PurpleMediaSession
43 gchar *id;
44 PurpleMedia *media;
45 PurpleMediaSessionType type;
46 gboolean initiator;
49 struct _PurpleMediaStream
51 PurpleMediaSession *session;
52 gchar *participant;
54 GList *local_candidates;
55 GList *remote_candidates;
57 gboolean initiator;
58 gboolean accepted;
59 gboolean candidates_prepared;
61 GList *active_local_candidates;
62 GList *active_remote_candidates;
64 #endif
66 struct _PurpleMediaPrivate
68 #ifdef USE_VV
69 PurpleMediaManager *manager;
70 PurpleAccount *account;
71 PurpleMediaBackend *backend;
72 gchar *conference_type;
73 gboolean initiator;
74 gpointer protocol_data;
76 GHashTable *sessions; /* PurpleMediaSession table */
77 GList *participants;
78 GList *streams; /* PurpleMediaStream table */
79 #else
80 gpointer dummy;
81 #endif
84 #ifdef USE_VV
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,
104 PurpleMedia *media);
105 static void purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
106 const gchar *sess_id, PurpleMedia *media);
108 static GObjectClass *parent_class = NULL;
112 enum {
113 S_ERROR,
114 CANDIDATES_PREPARED,
115 CODECS_CHANGED,
116 LEVEL,
117 NEW_CANDIDATE,
118 STATE_CHANGED,
119 STREAM_INFO,
120 CANDIDATE_PAIR_ESTABLISHED,
121 LAST_SIGNAL
123 static guint purple_media_signals[LAST_SIGNAL] = {0};
125 enum {
126 PROP_0,
127 PROP_MANAGER,
128 PROP_BACKEND,
129 PROP_ACCOUNT,
130 PROP_CONFERENCE_TYPE,
131 PROP_INITIATOR,
132 PROP_PROTOCOL_DATA,
134 #endif
137 GType
138 purple_media_get_type()
140 #ifdef USE_VV
141 static GType type = 0;
143 if (type == 0) {
144 static const GTypeInfo info = {
145 sizeof(PurpleMediaClass),
146 NULL,
147 NULL,
148 (GClassInitFunc) purple_media_class_init,
149 NULL,
150 NULL,
151 sizeof(PurpleMedia),
153 (GInstanceInitFunc) purple_media_init,
154 NULL
156 type = g_type_register_static(G_TYPE_OBJECT, "PurpleMedia", &info, 0);
158 return type;
159 #else
160 return G_TYPE_NONE;
161 #endif
164 #ifdef USE_VV
165 static void
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.",
192 G_TYPE_OBJECT,
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.",
198 PURPLE_TYPE_ACCOUNT,
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",
204 "Conference Type",
205 "The type of conference that this media object "
206 "has been created to provide.",
207 NULL,
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",
213 "initiator",
214 "If the local user initiated the conference.",
215 FALSE,
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",
221 "gpointer",
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,
231 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));
259 static void
260 purple_media_init (PurpleMedia *media)
262 media->priv = PURPLE_MEDIA_GET_PRIVATE(media);
263 memset(media->priv, 0, sizeof(*media->priv));
266 static void
267 purple_media_stream_free(PurpleMediaStream *stream)
269 if (stream == NULL)
270 return;
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);
286 g_free(stream);
289 static void
290 purple_media_session_free(PurpleMediaSession *session)
292 if (session == NULL)
293 return;
295 g_free(session->id);
296 g_free(session);
299 static void
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));
308 if (priv->backend) {
309 g_object_unref(priv->backend);
310 priv->backend = NULL;
313 if (priv->manager) {
314 g_object_unref(priv->manager);
315 priv->manager = NULL;
318 G_OBJECT_CLASS(parent_class)->dispose(media);
321 static void
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);
345 static void
346 purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
348 PurpleMedia *media;
349 g_return_if_fail(PURPLE_IS_MEDIA(object));
351 media = PURPLE_MEDIA(object);
353 switch (prop_id) {
354 case PROP_MANAGER:
355 media->priv->manager = g_value_dup_object(value);
356 break;
357 case PROP_ACCOUNT:
358 media->priv->account = g_value_get_object(value);
359 break;
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()),
366 "conference-type",
367 media->priv->conference_type,
368 "media", media,
369 NULL);
370 g_signal_connect(media->priv->backend,
371 "active-candidate-pair",
372 G_CALLBACK(
373 purple_media_candidate_pair_established_cb),
374 media);
375 g_signal_connect(media->priv->backend,
376 "candidates-prepared",
377 G_CALLBACK(
378 purple_media_candidates_prepared_cb),
379 media);
380 g_signal_connect(media->priv->backend,
381 "codecs-changed",
382 G_CALLBACK(
383 purple_media_codecs_changed_cb),
384 media);
385 g_signal_connect(media->priv->backend,
386 "new-candidate",
387 G_CALLBACK(
388 purple_media_new_local_candidate_cb),
389 media);
390 break;
391 case PROP_INITIATOR:
392 media->priv->initiator = g_value_get_boolean(value);
393 break;
394 case PROP_PROTOCOL_DATA:
395 media->priv->protocol_data = g_value_get_pointer(value);
396 break;
397 default:
398 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
399 break;
403 static void
404 purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
406 PurpleMedia *media;
407 g_return_if_fail(PURPLE_IS_MEDIA(object));
409 media = PURPLE_MEDIA(object);
411 switch (prop_id) {
412 case PROP_MANAGER:
413 g_value_set_object(value, media->priv->manager);
414 break;
415 case PROP_BACKEND:
416 g_value_set_object(value, media->priv->backend);
417 break;
418 case PROP_ACCOUNT:
419 g_value_set_object(value, media->priv->account);
420 break;
421 case PROP_CONFERENCE_TYPE:
422 g_value_set_string(value,
423 media->priv->conference_type);
424 break;
425 case PROP_INITIATOR:
426 g_value_set_boolean(value, media->priv->initiator);
427 break;
428 case PROP_PROTOCOL_DATA:
429 g_value_set_pointer(value, media->priv->protocol_data);
430 break;
431 default:
432 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
433 break;
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)
449 GList *streams;
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))
459 return stream;
462 return NULL;
465 static GList *
466 purple_media_get_streams(PurpleMedia *media, const gchar *session,
467 const gchar *participant)
469 GList *streams;
470 GList *ret = NULL;
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);
485 return ret;
488 static void
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,
497 g_free, NULL);
499 g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session);
502 #if 0
503 static gboolean
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);
509 #endif
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);
527 return media_stream;
530 static void
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);
541 #endif
543 GList *
544 purple_media_get_session_ids(PurpleMedia *media)
546 #ifdef USE_VV
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;
550 #else
551 return NULL;
552 #endif
555 #ifdef USE_VV
556 GstElement *
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);
568 #endif /* USE_VV */
570 PurpleAccount *
571 purple_media_get_account(PurpleMedia *media)
573 #ifdef USE_VV
574 PurpleAccount *account;
575 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
576 g_object_get(G_OBJECT(media), "account", &account, NULL);
577 return account;
578 #else
579 return NULL;
580 #endif
583 gpointer
584 purple_media_get_protocol_data(PurpleMedia *media)
586 #ifdef USE_VV
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;
591 #else
592 return NULL;
593 #endif
596 void
597 purple_media_set_protocol_data(PurpleMedia *media, gpointer protocol_data)
599 #ifdef USE_VV
600 g_return_if_fail(PURPLE_IS_MEDIA(media));
601 g_object_set(G_OBJECT(media), "protocol-data", protocol_data, NULL);
602 #endif
605 void
606 purple_media_error(PurpleMedia *media, const gchar *error, ...)
608 #ifdef USE_VV
609 va_list args;
610 gchar *message;
612 g_return_if_fail(PURPLE_IS_MEDIA(media));
614 va_start(args, error);
615 message = g_strdup_vprintf(error, args);
616 va_end(args);
618 purple_debug_error("media", "%s\n", message);
619 g_signal_emit(media, purple_media_signals[S_ERROR], 0, message);
621 g_free(message);
622 #endif
625 void
626 purple_media_end(PurpleMedia *media,
627 const gchar *session_id, const gchar *participant)
629 #ifdef USE_VV
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,
673 session->id, NULL);
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;
684 GList *tmp;
686 tmp = g_list_find_custom(participants,
687 stream->participant, (GCompareFunc)strcmp);
689 if (tmp != NULL) {
690 g_free(tmp->data);
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,
704 NULL, participant);
706 if (link != NULL) {
707 g_free(link->data);
708 media->priv->participants = g_list_delete_link(
709 media->priv->participants, link);
712 g_free(participant);
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,
720 NULL, NULL);
721 g_object_unref(media);
722 return;
724 #endif
727 void
728 purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
729 const gchar *session_id, const gchar *participant,
730 gboolean local)
732 #ifdef USE_VV
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;
750 g_signal_emit(media,
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,
757 stream->session);
759 if (g_list_find_custom(participants,
760 stream->participant,
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[
773 STREAM_INFO], 0,
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[
785 STREAM_INFO], 0,
786 PURPLE_MEDIA_INFO_ACCEPT,
787 NULL, participant, local);
789 g_free(participant);
792 /* Emit conference acceptance */
793 if (purple_media_accepted(media, NULL, NULL))
794 g_signal_emit(media,
795 purple_media_signals[STREAM_INFO],
796 0, PURPLE_MEDIA_INFO_ACCEPT,
797 NULL, NULL, local);
799 return;
800 } else if (type == PURPLE_MEDIA_INFO_HANGUP ||
801 type == PURPLE_MEDIA_INFO_REJECT) {
802 GList *streams;
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;
814 g_signal_emit(media,
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 */
852 g_signal_emit(media,
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(
859 media, session_id);
861 if (session == NULL) {
862 purple_debug_warning("media",
863 "Couldn't find session"
864 " to hangup/reject.\n");
865 } else {
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");
877 } else {
878 g_signal_emit(media, purple_media_signals[
879 STREAM_INFO], 0, type, NULL,
880 participant, local);
884 purple_media_end(media, session_id, participant);
885 return;
888 g_signal_emit(media, purple_media_signals[STREAM_INFO],
889 0, type, session_id, participant, local);
890 #endif
893 void
894 purple_media_set_params(PurpleMedia *media,
895 guint num_params, GParameter *params)
897 #ifdef USE_VV
898 g_return_if_fail(PURPLE_IS_MEDIA(media));
900 purple_media_backend_set_params(media->priv->backend, num_params, params);
901 #endif
904 const gchar **
905 purple_media_get_available_params(PurpleMedia *media)
907 static const gchar *NULL_ARRAY[] = { NULL };
908 #ifdef USE_VV
909 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL_ARRAY);
911 return purple_media_backend_get_available_params(media->priv->backend);
912 #else
913 return NULL_ARRAY;
914 #endif
917 gboolean
918 purple_media_param_is_supported(PurpleMedia *media, const gchar *param)
920 #ifdef USE_VV
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))
929 return TRUE;
930 #endif
931 return FALSE;
934 #ifdef USE_VV
935 static void
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);
950 static void
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],
962 0, sess_id, name);
965 /* callback called when a pair of transport candidates (local and remote)
966 * has been established */
967 static void
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,
972 PurpleMedia *media)
974 PurpleMediaStream *stream;
975 GList *iter;
976 guint id;
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)) {
987 g_object_unref(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(
993 local_candidate));
994 break;
997 if (iter == NULL)
998 stream->active_local_candidates = g_list_prepend(
999 stream->active_local_candidates,
1000 purple_media_candidate_copy(
1001 local_candidate));
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)) {
1009 g_object_unref(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(
1015 remote_candidate));
1016 break;
1019 if (iter == NULL)
1020 stream->active_remote_candidates = g_list_prepend(
1021 stream->active_remote_candidates,
1022 purple_media_candidate_copy(
1023 remote_candidate));
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");
1030 static void
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);
1036 #endif /* USE_VV */
1038 gboolean
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)
1044 #ifdef USE_VV
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");
1053 return FALSE;
1056 session = purple_media_get_session(media, sess_id);
1058 if (!session) {
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,
1068 session->id, NULL);
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,
1085 session->id, who);
1088 return TRUE;
1089 #else
1090 return FALSE;
1091 #endif /* USE_VV */
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);
1100 return ret;
1103 PurpleMediaSessionType
1104 purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id)
1106 #ifdef USE_VV
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;
1111 #else
1112 return PURPLE_MEDIA_NONE;
1113 #endif
1115 /* XXX: Should wait until codecs-ready is TRUE before using this function */
1116 GList *
1117 purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id)
1119 #ifdef USE_VV
1120 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1122 return purple_media_backend_get_codecs(media->priv->backend, sess_id);
1123 #else
1124 return NULL;
1125 #endif
1128 GList *
1129 purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id,
1130 const gchar *participant)
1132 #ifdef USE_VV
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);
1137 #else
1138 return NULL;
1139 #endif
1142 void
1143 purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id,
1144 const gchar *participant,
1145 GList *remote_candidates)
1147 #ifdef USE_VV
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)");
1159 return;
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);
1167 #endif
1170 GList *
1171 purple_media_get_active_local_candidates(PurpleMedia *media,
1172 const gchar *sess_id, const gchar *participant)
1174 #ifdef USE_VV
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);
1180 #else
1181 return NULL;
1182 #endif
1185 GList *
1186 purple_media_get_active_remote_candidates(PurpleMedia *media,
1187 const gchar *sess_id, const gchar *participant)
1189 #ifdef USE_VV
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);
1195 #else
1196 return NULL;
1197 #endif
1200 gboolean
1201 purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
1202 const gchar *participant, GList *codecs)
1204 #ifdef USE_VV
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);
1209 #else
1210 return FALSE;
1211 #endif
1214 gboolean
1215 purple_media_candidates_prepared(PurpleMedia *media,
1216 const gchar *session_id, const gchar *participant)
1218 #ifdef USE_VV
1219 GList *streams;
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);
1230 prepared = FALSE;
1231 break;
1235 return prepared;
1236 #else
1237 return FALSE;
1238 #endif
1241 gboolean
1242 purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec)
1244 #ifdef USE_VV
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);
1249 #else
1250 return FALSE;
1251 #endif
1254 gboolean
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)
1259 #ifdef USE_VV
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);
1263 #else
1264 return FALSE;
1265 #endif
1268 gboolean
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)
1273 #ifdef USE_VV
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);
1277 #else
1278 return FALSE;
1279 #endif
1282 gboolean
1283 purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id)
1285 #ifdef USE_VV
1286 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1288 return purple_media_backend_codecs_ready(
1289 media->priv->backend, sess_id);
1290 #else
1291 return FALSE;
1292 #endif
1295 gboolean
1296 purple_media_set_send_rtcp_mux(PurpleMedia *media, const gchar *sess_id,
1297 const gchar *participant, gboolean send_rtcp_mux)
1299 #ifdef USE_VV
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);
1304 #else
1305 return FALSE;
1306 #endif
1309 gboolean
1310 purple_media_is_initiator(PurpleMedia *media,
1311 const gchar *sess_id, const gchar *participant)
1313 #ifdef USE_VV
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;
1327 #endif
1328 return FALSE;
1331 gboolean
1332 purple_media_accepted(PurpleMedia *media, const gchar *sess_id,
1333 const gchar *participant)
1335 #ifdef USE_VV
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) {
1346 accepted = FALSE;
1347 break;
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);
1358 accepted = FALSE;
1359 break;
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)
1366 accepted = FALSE;
1369 return accepted;
1370 #else
1371 return FALSE;
1372 #endif
1375 void purple_media_set_input_volume(PurpleMedia *media,
1376 const gchar *session_id, double level)
1378 #ifdef USE_VV
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),
1385 session_id, level);
1386 #endif
1389 void purple_media_set_output_volume(PurpleMedia *media,
1390 const gchar *session_id, const gchar *participant,
1391 double level)
1393 #ifdef USE_VV
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);
1401 #endif
1404 gulong
1405 purple_media_set_output_window(PurpleMedia *media, const gchar *session_id,
1406 const gchar *participant, gulong window_id)
1408 #ifdef USE_VV
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);
1413 #else
1414 return 0;
1415 #endif
1418 void
1419 purple_media_remove_output_windows(PurpleMedia *media)
1421 #ifdef USE_VV
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);
1437 #endif
1440 #ifdef USE_VV
1441 GstElement *
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);
1454 #endif /* USE_VV */
1456 gboolean
1457 purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id,
1458 gchar dtmf, guint8 volume, guint16 duration)
1460 #ifdef USE_VV
1461 PurpleMediaBackendIface *backend_iface = NULL;
1463 if (media)
1465 backend_iface = PURPLE_MEDIA_BACKEND_GET_INTERFACE(media->priv->backend);
1468 if (dtmf == 'a')
1469 dtmf = 'A';
1470 else if (dtmf == 'b')
1471 dtmf = 'B';
1472 else if (dtmf == 'c')
1473 dtmf = 'C';
1474 else if (dtmf == 'd')
1475 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))
1483 return TRUE;
1485 #endif
1486 return FALSE;