Merged in qulogic/pidgin (pull request #607)
[pidgin-git.git] / libpurple / media.c
blob8fdd5f9315550b5fd3c87357ae7bd5a304e2b797
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 static void purple_media_class_init (PurpleMediaClass *klass);
86 static void purple_media_init (PurpleMedia *media);
87 static void purple_media_dispose (GObject *object);
88 static void purple_media_finalize (GObject *object);
89 static void purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
90 static void purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
92 static void purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
93 const gchar *sess_id, const gchar *participant,
94 PurpleMediaCandidate *candidate, PurpleMedia *media);
95 static void purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
96 const gchar *sess_id, const gchar *name, PurpleMedia *media);
97 static void purple_media_candidate_pair_established_cb(
98 PurpleMediaBackend *backend,
99 const gchar *sess_id, const gchar *name,
100 PurpleMediaCandidate *local_candidate,
101 PurpleMediaCandidate *remote_candidate,
102 PurpleMedia *media);
103 static void purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
104 const gchar *sess_id, PurpleMedia *media);
107 enum {
108 S_ERROR,
109 CANDIDATES_PREPARED,
110 CODECS_CHANGED,
111 LEVEL,
112 NEW_CANDIDATE,
113 STATE_CHANGED,
114 STREAM_INFO,
115 CANDIDATE_PAIR_ESTABLISHED,
116 LAST_SIGNAL
118 static guint purple_media_signals[LAST_SIGNAL] = {0};
120 enum {
121 PROP_0,
122 PROP_MANAGER,
123 PROP_BACKEND,
124 PROP_ACCOUNT,
125 PROP_CONFERENCE_TYPE,
126 PROP_INITIATOR,
127 PROP_PROTOCOL_DATA,
130 G_DEFINE_TYPE_WITH_PRIVATE(PurpleMedia, purple_media, G_TYPE_OBJECT);
131 #else
132 GType
133 purple_media_get_type()
135 return G_TYPE_NONE;
137 #endif /* USE_VV */
139 #ifdef USE_VV
140 static void
141 purple_media_class_init (PurpleMediaClass *klass)
143 GObjectClass *gobject_class = (GObjectClass*)klass;
145 gobject_class->dispose = purple_media_dispose;
146 gobject_class->finalize = purple_media_finalize;
147 gobject_class->set_property = purple_media_set_property;
148 gobject_class->get_property = purple_media_get_property;
150 g_object_class_install_property(gobject_class, PROP_MANAGER,
151 g_param_spec_object("manager",
152 "Purple Media Manager",
153 "The media manager that contains this media session.",
154 PURPLE_TYPE_MEDIA_MANAGER,
155 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
156 G_PARAM_STATIC_STRINGS));
159 * This one should be PURPLE_TYPE_MEDIA_BACKEND, but it doesn't
160 * like interfaces because they "aren't GObjects"
162 g_object_class_install_property(gobject_class, PROP_BACKEND,
163 g_param_spec_object("backend",
164 "Purple Media Backend",
165 "The backend object this media object uses.",
166 G_TYPE_OBJECT,
167 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
169 g_object_class_install_property(gobject_class, PROP_ACCOUNT,
170 g_param_spec_object("account", "PurpleAccount",
171 "The account this media session is on.",
172 PURPLE_TYPE_ACCOUNT,
173 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
174 G_PARAM_STATIC_STRINGS));
176 g_object_class_install_property(gobject_class, PROP_CONFERENCE_TYPE,
177 g_param_spec_string("conference-type",
178 "Conference Type",
179 "The type of conference that this media object "
180 "has been created to provide.",
181 NULL,
182 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
183 G_PARAM_STATIC_STRINGS));
185 g_object_class_install_property(gobject_class, PROP_INITIATOR,
186 g_param_spec_boolean("initiator",
187 "initiator",
188 "If the local user initiated the conference.",
189 FALSE,
190 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
191 G_PARAM_STATIC_STRINGS));
193 g_object_class_install_property(gobject_class, PROP_PROTOCOL_DATA,
194 g_param_spec_pointer("protocol-data",
195 "gpointer",
196 "Data the protocol set on the media session.",
197 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
199 purple_media_signals[S_ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(klass),
200 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
201 G_TYPE_NONE, 1, G_TYPE_STRING);
202 purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass),
203 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
204 G_TYPE_NONE, 2, G_TYPE_STRING,
205 G_TYPE_STRING);
206 purple_media_signals[CODECS_CHANGED] = g_signal_new("codecs-changed", G_TYPE_FROM_CLASS(klass),
207 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
208 G_TYPE_NONE, 1, G_TYPE_STRING);
209 purple_media_signals[LEVEL] = g_signal_new("level", G_TYPE_FROM_CLASS(klass),
210 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
211 G_TYPE_NONE, 3, G_TYPE_STRING,
212 G_TYPE_STRING, G_TYPE_DOUBLE);
213 purple_media_signals[NEW_CANDIDATE] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass),
214 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
215 G_TYPE_NONE, 3, G_TYPE_POINTER,
216 G_TYPE_POINTER, PURPLE_TYPE_MEDIA_CANDIDATE);
217 purple_media_signals[STATE_CHANGED] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass),
218 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
219 G_TYPE_NONE, 3, PURPLE_MEDIA_TYPE_STATE,
220 G_TYPE_STRING, G_TYPE_STRING);
221 purple_media_signals[STREAM_INFO] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass),
222 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
223 G_TYPE_NONE, 4, PURPLE_MEDIA_TYPE_INFO_TYPE,
224 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
225 purple_media_signals[CANDIDATE_PAIR_ESTABLISHED] = g_signal_new("candidate-pair-established", G_TYPE_FROM_CLASS(klass),
226 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL,
227 G_TYPE_NONE, 4, G_TYPE_POINTER, G_TYPE_POINTER,
228 PURPLE_TYPE_MEDIA_CANDIDATE, PURPLE_TYPE_MEDIA_CANDIDATE);
232 static void
233 purple_media_init (PurpleMedia *media)
235 media->priv = purple_media_get_instance_private(media);
236 memset(media->priv, 0, sizeof(*media->priv));
239 static void
240 purple_media_stream_free(PurpleMediaStream *stream)
242 if (stream == NULL)
243 return;
245 g_free(stream->participant);
247 if (stream->local_candidates)
248 purple_media_candidate_list_free(stream->local_candidates);
249 if (stream->remote_candidates)
250 purple_media_candidate_list_free(stream->remote_candidates);
252 if (stream->active_local_candidates)
253 purple_media_candidate_list_free(
254 stream->active_local_candidates);
255 if (stream->active_remote_candidates)
256 purple_media_candidate_list_free(
257 stream->active_remote_candidates);
259 g_free(stream);
262 static void
263 purple_media_session_free(PurpleMediaSession *session)
265 if (session == NULL)
266 return;
268 g_free(session->id);
269 g_free(session);
272 static void
273 purple_media_dispose(GObject *media)
275 PurpleMediaPrivate *priv =
276 purple_media_get_instance_private(PURPLE_MEDIA(media));
278 purple_debug_info("media","purple_media_dispose\n");
280 purple_media_manager_remove_media(priv->manager, PURPLE_MEDIA(media));
282 if (priv->backend) {
283 g_object_unref(priv->backend);
284 priv->backend = NULL;
287 if (priv->manager) {
288 g_object_unref(priv->manager);
289 priv->manager = NULL;
292 G_OBJECT_CLASS(purple_media_parent_class)->dispose(media);
295 static void
296 purple_media_finalize(GObject *media)
298 PurpleMediaPrivate *priv =
299 purple_media_get_instance_private(PURPLE_MEDIA(media));
300 purple_debug_info("media","purple_media_finalize\n");
302 for (; priv->streams; priv->streams = g_list_delete_link(priv->streams, priv->streams))
303 purple_media_stream_free(priv->streams->data);
305 for (; priv->participants; priv->participants = g_list_delete_link(
306 priv->participants, priv->participants))
307 g_free(priv->participants->data);
309 if (priv->sessions) {
310 GList *sessions = g_hash_table_get_values(priv->sessions);
311 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
312 purple_media_session_free(sessions->data);
314 g_hash_table_destroy(priv->sessions);
317 G_OBJECT_CLASS(purple_media_parent_class)->finalize(media);
320 static void
321 purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
323 PurpleMedia *media;
324 g_return_if_fail(PURPLE_IS_MEDIA(object));
326 media = PURPLE_MEDIA(object);
328 switch (prop_id) {
329 case PROP_MANAGER:
330 media->priv->manager = g_value_dup_object(value);
331 break;
332 case PROP_ACCOUNT:
333 media->priv->account = g_value_get_object(value);
334 break;
335 case PROP_CONFERENCE_TYPE:
336 media->priv->conference_type =
337 g_value_dup_string(value);
338 media->priv->backend = g_object_new(
339 purple_media_manager_get_backend_type(
340 purple_media_manager_get()),
341 "conference-type",
342 media->priv->conference_type,
343 "media", media,
344 NULL);
345 g_signal_connect(media->priv->backend,
346 "active-candidate-pair",
347 G_CALLBACK(
348 purple_media_candidate_pair_established_cb),
349 media);
350 g_signal_connect(media->priv->backend,
351 "candidates-prepared",
352 G_CALLBACK(
353 purple_media_candidates_prepared_cb),
354 media);
355 g_signal_connect(media->priv->backend,
356 "codecs-changed",
357 G_CALLBACK(
358 purple_media_codecs_changed_cb),
359 media);
360 g_signal_connect(media->priv->backend,
361 "new-candidate",
362 G_CALLBACK(
363 purple_media_new_local_candidate_cb),
364 media);
365 break;
366 case PROP_INITIATOR:
367 media->priv->initiator = g_value_get_boolean(value);
368 break;
369 case PROP_PROTOCOL_DATA:
370 media->priv->protocol_data = g_value_get_pointer(value);
371 break;
372 default:
373 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
374 break;
378 static void
379 purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
381 PurpleMedia *media;
382 g_return_if_fail(PURPLE_IS_MEDIA(object));
384 media = PURPLE_MEDIA(object);
386 switch (prop_id) {
387 case PROP_MANAGER:
388 g_value_set_object(value, media->priv->manager);
389 break;
390 case PROP_BACKEND:
391 g_value_set_object(value, media->priv->backend);
392 break;
393 case PROP_ACCOUNT:
394 g_value_set_object(value, media->priv->account);
395 break;
396 case PROP_CONFERENCE_TYPE:
397 g_value_set_string(value,
398 media->priv->conference_type);
399 break;
400 case PROP_INITIATOR:
401 g_value_set_boolean(value, media->priv->initiator);
402 break;
403 case PROP_PROTOCOL_DATA:
404 g_value_set_pointer(value, media->priv->protocol_data);
405 break;
406 default:
407 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
408 break;
413 static PurpleMediaSession*
414 purple_media_get_session(PurpleMedia *media, const gchar *sess_id)
416 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
417 return (PurpleMediaSession*) (media->priv->sessions) ?
418 g_hash_table_lookup(media->priv->sessions, sess_id) : NULL;
421 static PurpleMediaStream*
422 purple_media_get_stream(PurpleMedia *media, const gchar *session, const gchar *participant)
424 GList *streams;
426 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
428 streams = media->priv->streams;
430 for (; streams; streams = g_list_next(streams)) {
431 PurpleMediaStream *stream = streams->data;
432 if (purple_strequal(stream->session->id, session) &&
433 purple_strequal(stream->participant, participant))
434 return stream;
437 return NULL;
440 static GList *
441 purple_media_get_streams(PurpleMedia *media, const gchar *session,
442 const gchar *participant)
444 GList *streams;
445 GList *ret = NULL;
447 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
449 streams = media->priv->streams;
451 for (; streams; streams = g_list_next(streams)) {
452 PurpleMediaStream *stream = streams->data;
453 if ((session == NULL ||
454 purple_strequal(stream->session->id, session)) &&
455 (participant == NULL ||
456 purple_strequal(stream->participant, participant)))
457 ret = g_list_append(ret, stream);
460 return ret;
463 static void
464 purple_media_add_session(PurpleMedia *media, PurpleMediaSession *session)
466 g_return_if_fail(PURPLE_IS_MEDIA(media));
467 g_return_if_fail(session != NULL);
469 if (!media->priv->sessions) {
470 purple_debug_info("media", "Creating hash table for sessions\n");
471 media->priv->sessions = g_hash_table_new_full(g_str_hash, g_str_equal,
472 g_free, NULL);
474 g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session);
477 static PurpleMediaStream *
478 purple_media_insert_stream(PurpleMediaSession *session,
479 const gchar *name, gboolean initiator)
481 PurpleMediaStream *media_stream;
483 g_return_val_if_fail(session != NULL, NULL);
485 media_stream = g_new0(PurpleMediaStream, 1);
486 media_stream->participant = g_strdup(name);
487 media_stream->session = session;
488 media_stream->initiator = initiator;
490 session->media->priv->streams =
491 g_list_append(session->media->priv->streams, media_stream);
493 return media_stream;
496 static void
497 purple_media_insert_local_candidate(PurpleMediaSession *session, const gchar *name,
498 PurpleMediaCandidate *candidate)
500 PurpleMediaStream *stream;
502 g_return_if_fail(session != NULL);
504 stream = purple_media_get_stream(session->media, session->id, name);
505 stream->local_candidates = g_list_append(stream->local_candidates, candidate);
507 #endif
509 GList *
510 purple_media_get_session_ids(PurpleMedia *media)
512 #ifdef USE_VV
513 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
514 return media->priv->sessions != NULL ?
515 g_hash_table_get_keys(media->priv->sessions) : NULL;
516 #else
517 return NULL;
518 #endif
521 #ifdef USE_VV
522 GstElement *
523 purple_media_get_src(PurpleMedia *media, const gchar *sess_id)
525 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
527 if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
528 return purple_media_backend_fs2_get_src(
529 PURPLE_MEDIA_BACKEND_FS2(
530 media->priv->backend), sess_id);
532 g_return_val_if_reached(NULL);
534 #endif /* USE_VV */
536 PurpleAccount *
537 purple_media_get_account(PurpleMedia *media)
539 #ifdef USE_VV
540 PurpleAccount *account;
541 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
542 g_object_get(G_OBJECT(media), "account", &account, NULL);
543 return account;
544 #else
545 return NULL;
546 #endif
549 gpointer
550 purple_media_get_protocol_data(PurpleMedia *media)
552 #ifdef USE_VV
553 gpointer protocol_data;
554 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
555 g_object_get(G_OBJECT(media), "protocol-data", &protocol_data, NULL);
556 return protocol_data;
557 #else
558 return NULL;
559 #endif
562 void
563 purple_media_set_protocol_data(PurpleMedia *media, gpointer protocol_data)
565 #ifdef USE_VV
566 g_return_if_fail(PURPLE_IS_MEDIA(media));
567 g_object_set(G_OBJECT(media), "protocol-data", protocol_data, NULL);
568 #endif
571 void
572 purple_media_error(PurpleMedia *media, const gchar *error, ...)
574 #ifdef USE_VV
575 va_list args;
576 gchar *message;
578 g_return_if_fail(PURPLE_IS_MEDIA(media));
580 va_start(args, error);
581 message = g_strdup_vprintf(error, args);
582 va_end(args);
584 purple_debug_error("media", "%s\n", message);
585 g_signal_emit(media, purple_media_signals[S_ERROR], 0, message);
587 g_free(message);
588 #endif
591 void
592 purple_media_end(PurpleMedia *media,
593 const gchar *session_id, const gchar *participant)
595 #ifdef USE_VV
596 GList *iter, *sessions = NULL, *participants = NULL;
598 g_return_if_fail(PURPLE_IS_MEDIA(media));
600 iter = purple_media_get_streams(media, session_id, participant);
602 /* Free matching streams */
603 for (; iter; iter = g_list_delete_link(iter, iter)) {
604 PurpleMediaStream *stream = iter->data;
606 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
607 0, PURPLE_MEDIA_STATE_END,
608 stream->session->id, stream->participant);
610 media->priv->streams =
611 g_list_remove(media->priv->streams, stream);
613 if (g_list_find(sessions, stream->session) == NULL)
614 sessions = g_list_prepend(sessions, stream->session);
616 if (g_list_find_custom(participants, stream->participant,
617 (GCompareFunc)strcmp) == NULL)
618 participants = g_list_prepend(participants,
619 g_strdup(stream->participant));
621 purple_media_stream_free(stream);
624 iter = media->priv->streams;
626 /* Reduce to list of sessions to remove */
627 for (; iter; iter = g_list_next(iter)) {
628 PurpleMediaStream *stream = iter->data;
630 sessions = g_list_remove(sessions, stream->session);
633 /* Free sessions with no streams left */
634 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
635 PurpleMediaSession *session = sessions->data;
637 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
638 0, PURPLE_MEDIA_STATE_END,
639 session->id, NULL);
641 g_hash_table_remove(media->priv->sessions, session->id);
642 purple_media_session_free(session);
645 iter = media->priv->streams;
647 /* Reduce to list of participants to remove */
648 for (; iter; iter = g_list_next(iter)) {
649 PurpleMediaStream *stream = iter->data;
650 GList *tmp;
652 tmp = g_list_find_custom(participants,
653 stream->participant, (GCompareFunc)strcmp);
655 if (tmp != NULL) {
656 g_free(tmp->data);
657 participants = g_list_delete_link(participants, tmp);
661 /* Remove participants with no streams left (just emit the signal) */
662 for (; participants; participants =
663 g_list_delete_link(participants, participants)) {
664 gchar *participant = participants->data;
665 GList *link = g_list_find_custom(media->priv->participants,
666 participant, (GCompareFunc)strcmp);
668 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
669 0, PURPLE_MEDIA_STATE_END,
670 NULL, participant);
672 if (link != NULL) {
673 g_free(link->data);
674 media->priv->participants = g_list_delete_link(
675 media->priv->participants, link);
678 g_free(participant);
681 /* Free the conference if no sessions left */
682 if (media->priv->sessions != NULL &&
683 g_hash_table_size(media->priv->sessions) == 0) {
684 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
685 0, PURPLE_MEDIA_STATE_END,
686 NULL, NULL);
687 g_object_unref(media);
688 return;
690 #endif
693 void
694 purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
695 const gchar *session_id, const gchar *participant,
696 gboolean local)
698 #ifdef USE_VV
699 g_return_if_fail(PURPLE_IS_MEDIA(media));
701 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
702 GList *streams, *sessions = NULL, *participants = NULL;
704 g_return_if_fail(PURPLE_IS_MEDIA(media));
706 streams = purple_media_get_streams(media,
707 session_id, participant);
709 /* Emit stream acceptance */
710 for (; streams; streams =
711 g_list_delete_link(streams, streams)) {
712 PurpleMediaStream *stream = streams->data;
714 stream->accepted = TRUE;
716 g_signal_emit(media,
717 purple_media_signals[STREAM_INFO],
718 0, type, stream->session->id,
719 stream->participant, local);
721 if (g_list_find(sessions, stream->session) == NULL)
722 sessions = g_list_prepend(sessions,
723 stream->session);
725 if (g_list_find_custom(participants,
726 stream->participant,
727 (GCompareFunc)strcmp) == NULL)
728 participants = g_list_prepend(participants,
729 g_strdup(stream->participant));
732 /* Emit session acceptance */
733 for (; sessions; sessions =
734 g_list_delete_link(sessions, sessions)) {
735 PurpleMediaSession *session = sessions->data;
737 if (purple_media_accepted(media, session->id, NULL))
738 g_signal_emit(media, purple_media_signals[
739 STREAM_INFO], 0,
740 PURPLE_MEDIA_INFO_ACCEPT,
741 session->id, NULL, local);
744 /* Emit participant acceptance */
745 for (; participants; participants = g_list_delete_link(
746 participants, participants)) {
747 gchar *participant = participants->data;
749 if (purple_media_accepted(media, NULL, participant))
750 g_signal_emit(media, purple_media_signals[
751 STREAM_INFO], 0,
752 PURPLE_MEDIA_INFO_ACCEPT,
753 NULL, participant, local);
755 g_free(participant);
758 /* Emit conference acceptance */
759 if (purple_media_accepted(media, NULL, NULL))
760 g_signal_emit(media,
761 purple_media_signals[STREAM_INFO],
762 0, PURPLE_MEDIA_INFO_ACCEPT,
763 NULL, NULL, local);
765 return;
766 } else if (type == PURPLE_MEDIA_INFO_HANGUP ||
767 type == PURPLE_MEDIA_INFO_REJECT) {
768 GList *streams;
770 g_return_if_fail(PURPLE_IS_MEDIA(media));
772 streams = purple_media_get_streams(media,
773 session_id, participant);
775 /* Emit for stream */
776 for (; streams; streams =
777 g_list_delete_link(streams, streams)) {
778 PurpleMediaStream *stream = streams->data;
780 g_signal_emit(media,
781 purple_media_signals[STREAM_INFO],
782 0, type, stream->session->id,
783 stream->participant, local);
786 if (session_id != NULL && participant != NULL) {
787 /* Everything that needs to be emitted has been */
788 } else if (session_id == NULL && participant == NULL) {
789 /* Emit for everything in the conference */
790 GList *sessions = NULL;
791 GList *participants = media->priv->participants;
793 if (media->priv->sessions != NULL)
794 sessions = g_hash_table_get_values(
795 media->priv->sessions);
797 /* Emit for sessions */
798 for (; sessions; sessions = g_list_delete_link(
799 sessions, sessions)) {
800 PurpleMediaSession *session = sessions->data;
802 g_signal_emit(media, purple_media_signals[
803 STREAM_INFO], 0, type,
804 session->id, NULL, local);
807 /* Emit for participants */
808 for (; participants; participants =
809 g_list_next(participants)) {
810 gchar *participant = participants->data;
812 g_signal_emit(media, purple_media_signals[
813 STREAM_INFO], 0, type,
814 NULL, participant, local);
817 /* Emit for conference */
818 g_signal_emit(media,
819 purple_media_signals[STREAM_INFO],
820 0, type, NULL, NULL, local);
821 } else if (session_id != NULL) {
822 /* Emit just the specific session */
823 PurpleMediaSession *session =
824 purple_media_get_session(
825 media, session_id);
827 if (session == NULL) {
828 purple_debug_warning("media",
829 "Couldn't find session"
830 " to hangup/reject.\n");
831 } else {
832 g_signal_emit(media, purple_media_signals[
833 STREAM_INFO], 0, type,
834 session->id, NULL, local);
836 } else if (participant != NULL) {
837 /* Emit just the specific participant */
838 if (!g_list_find_custom(media->priv->participants,
839 participant, (GCompareFunc)strcmp)) {
840 purple_debug_warning("media",
841 "Couldn't find participant"
842 " to hangup/reject.\n");
843 } else {
844 g_signal_emit(media, purple_media_signals[
845 STREAM_INFO], 0, type, NULL,
846 participant, local);
850 purple_media_end(media, session_id, participant);
851 return;
854 g_signal_emit(media, purple_media_signals[STREAM_INFO],
855 0, type, session_id, participant, local);
856 #endif
859 void
860 purple_media_set_params(PurpleMedia *media,
861 guint num_params, GParameter *params)
863 #ifdef USE_VV
864 g_return_if_fail(PURPLE_IS_MEDIA(media));
866 purple_media_backend_set_params(media->priv->backend, num_params, params);
867 #endif
870 const gchar **
871 purple_media_get_available_params(PurpleMedia *media)
873 static const gchar *NULL_ARRAY[] = { NULL };
874 #ifdef USE_VV
875 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL_ARRAY);
877 return purple_media_backend_get_available_params(media->priv->backend);
878 #else
879 return NULL_ARRAY;
880 #endif
883 gboolean
884 purple_media_param_is_supported(PurpleMedia *media, const gchar *param)
886 #ifdef USE_VV
887 const gchar **params;
889 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
890 g_return_val_if_fail(param != NULL, FALSE);
892 params = purple_media_backend_get_available_params(media->priv->backend);
893 for (; *params != NULL; ++params)
894 if (purple_strequal(*params, param))
895 return TRUE;
896 #endif
897 return FALSE;
900 #ifdef USE_VV
901 static void
902 purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
903 const gchar *sess_id, const gchar *participant,
904 PurpleMediaCandidate *candidate, PurpleMedia *media)
906 PurpleMediaSession *session =
907 purple_media_get_session(media, sess_id);
909 purple_media_insert_local_candidate(session, participant,
910 purple_media_candidate_copy(candidate));
912 g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE],
913 0, session->id, participant, candidate);
916 static void
917 purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
918 const gchar *sess_id, const gchar *name, PurpleMedia *media)
920 PurpleMediaStream *stream_data;
922 g_return_if_fail(PURPLE_IS_MEDIA(media));
924 stream_data = purple_media_get_stream(media, sess_id, name);
925 stream_data->candidates_prepared = TRUE;
927 g_signal_emit(media, purple_media_signals[CANDIDATES_PREPARED],
928 0, sess_id, name);
931 /* callback called when a pair of transport candidates (local and remote)
932 * has been established */
933 static void
934 purple_media_candidate_pair_established_cb(PurpleMediaBackend *backend,
935 const gchar *sess_id, const gchar *name,
936 PurpleMediaCandidate *local_candidate,
937 PurpleMediaCandidate *remote_candidate,
938 PurpleMedia *media)
940 PurpleMediaStream *stream;
941 GList *iter;
942 guint id;
944 g_return_if_fail(PURPLE_IS_MEDIA(media));
946 stream = purple_media_get_stream(media, sess_id, name);
947 id = purple_media_candidate_get_component_id(local_candidate);
949 iter = stream->active_local_candidates;
950 for(; iter; iter = g_list_next(iter)) {
951 PurpleMediaCandidate *c = iter->data;
952 if (id == purple_media_candidate_get_component_id(c)) {
953 g_object_unref(c);
954 stream->active_local_candidates =
955 g_list_delete_link(iter, iter);
956 stream->active_local_candidates = g_list_prepend(
957 stream->active_local_candidates,
958 purple_media_candidate_copy(
959 local_candidate));
960 break;
963 if (iter == NULL)
964 stream->active_local_candidates = g_list_prepend(
965 stream->active_local_candidates,
966 purple_media_candidate_copy(
967 local_candidate));
969 id = purple_media_candidate_get_component_id(local_candidate);
971 iter = stream->active_remote_candidates;
972 for(; iter; iter = g_list_next(iter)) {
973 PurpleMediaCandidate *c = iter->data;
974 if (id == purple_media_candidate_get_component_id(c)) {
975 g_object_unref(c);
976 stream->active_remote_candidates =
977 g_list_delete_link(iter, iter);
978 stream->active_remote_candidates = g_list_prepend(
979 stream->active_remote_candidates,
980 purple_media_candidate_copy(
981 remote_candidate));
982 break;
985 if (iter == NULL)
986 stream->active_remote_candidates = g_list_prepend(
987 stream->active_remote_candidates,
988 purple_media_candidate_copy(
989 remote_candidate));
991 g_signal_emit(media, purple_media_signals[CANDIDATE_PAIR_ESTABLISHED],
992 0, sess_id, name, local_candidate, remote_candidate);
993 purple_debug_info("media", "candidate pair established\n");
996 static void
997 purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
998 const gchar *sess_id, PurpleMedia *media)
1000 g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, sess_id);
1002 #endif /* USE_VV */
1004 gboolean
1005 purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
1006 const gchar *who, PurpleMediaSessionType type,
1007 gboolean initiator, const gchar *transmitter,
1008 guint num_params, GParameter *params)
1010 #ifdef USE_VV
1011 PurpleMediaSession *session;
1013 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1015 if (!purple_media_backend_add_stream(media->priv->backend,
1016 sess_id, who, type, initiator, transmitter,
1017 num_params, params)) {
1018 purple_debug_error("media", "Error adding stream.\n");
1019 return FALSE;
1022 session = purple_media_get_session(media, sess_id);
1024 if (!session) {
1025 session = g_new0(PurpleMediaSession, 1);
1026 session->id = g_strdup(sess_id);
1027 session->media = media;
1028 session->type = type;
1029 session->initiator = initiator;
1031 purple_media_add_session(media, session);
1032 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
1033 0, PURPLE_MEDIA_STATE_NEW,
1034 session->id, NULL);
1037 if (!g_list_find_custom(media->priv->participants,
1038 who, (GCompareFunc)strcmp)) {
1039 media->priv->participants = g_list_prepend(
1040 media->priv->participants, g_strdup(who));
1042 g_signal_emit(media, purple_media_signals[STATE_CHANGED], 0,
1043 PURPLE_MEDIA_STATE_NEW, NULL, who);
1046 if (purple_media_get_stream(media, sess_id, who) == NULL) {
1047 purple_media_insert_stream(session, who, initiator);
1049 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
1050 0, PURPLE_MEDIA_STATE_NEW,
1051 session->id, who);
1054 return TRUE;
1055 #else
1056 return FALSE;
1057 #endif /* USE_VV */
1060 PurpleMediaManager *
1061 purple_media_get_manager(PurpleMedia *media)
1063 PurpleMediaManager *ret;
1064 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1065 g_object_get(media, "manager", &ret, NULL);
1066 return ret;
1069 PurpleMediaSessionType
1070 purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id)
1072 #ifdef USE_VV
1073 PurpleMediaSession *session;
1074 g_return_val_if_fail(PURPLE_IS_MEDIA(media), PURPLE_MEDIA_NONE);
1075 session = purple_media_get_session(media, sess_id);
1076 return session->type;
1077 #else
1078 return PURPLE_MEDIA_NONE;
1079 #endif
1081 /* XXX: Should wait until codecs-ready is TRUE before using this function */
1082 GList *
1083 purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id)
1085 #ifdef USE_VV
1086 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1088 return purple_media_backend_get_codecs(media->priv->backend, sess_id);
1089 #else
1090 return NULL;
1091 #endif
1094 GList *
1095 purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id,
1096 const gchar *participant)
1098 #ifdef USE_VV
1099 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1101 return purple_media_backend_get_local_candidates(media->priv->backend,
1102 sess_id, participant);
1103 #else
1104 return NULL;
1105 #endif
1108 void
1109 purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id,
1110 const gchar *participant,
1111 GList *remote_candidates)
1113 #ifdef USE_VV
1114 PurpleMediaStream *stream;
1116 g_return_if_fail(PURPLE_IS_MEDIA(media));
1117 stream = purple_media_get_stream(media, sess_id, participant);
1119 if (stream == NULL) {
1120 purple_debug_error("media",
1121 "purple_media_add_remote_candidates: "
1122 "couldn't find stream %s %s.\n",
1123 sess_id ? sess_id : "(null)",
1124 participant ? participant : "(null)");
1125 return;
1128 stream->remote_candidates = g_list_concat(stream->remote_candidates,
1129 purple_media_candidate_list_copy(remote_candidates));
1131 purple_media_backend_add_remote_candidates(media->priv->backend,
1132 sess_id, participant, remote_candidates);
1133 #endif
1136 GList *
1137 purple_media_get_active_local_candidates(PurpleMedia *media,
1138 const gchar *sess_id, const gchar *participant)
1140 #ifdef USE_VV
1141 PurpleMediaStream *stream;
1142 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1143 stream = purple_media_get_stream(media, sess_id, participant);
1144 return purple_media_candidate_list_copy(
1145 stream->active_local_candidates);
1146 #else
1147 return NULL;
1148 #endif
1151 GList *
1152 purple_media_get_active_remote_candidates(PurpleMedia *media,
1153 const gchar *sess_id, const gchar *participant)
1155 #ifdef USE_VV
1156 PurpleMediaStream *stream;
1157 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1158 stream = purple_media_get_stream(media, sess_id, participant);
1159 return purple_media_candidate_list_copy(
1160 stream->active_remote_candidates);
1161 #else
1162 return NULL;
1163 #endif
1166 gboolean
1167 purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
1168 const gchar *participant, GList *codecs)
1170 #ifdef USE_VV
1171 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1173 return purple_media_backend_set_remote_codecs(media->priv->backend,
1174 sess_id, participant, codecs);
1175 #else
1176 return FALSE;
1177 #endif
1180 gboolean
1181 purple_media_candidates_prepared(PurpleMedia *media,
1182 const gchar *session_id, const gchar *participant)
1184 #ifdef USE_VV
1185 GList *streams;
1186 gboolean prepared = TRUE;
1188 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1190 streams = purple_media_get_streams(media, session_id, participant);
1192 for (; streams; streams = g_list_delete_link(streams, streams)) {
1193 PurpleMediaStream *stream = streams->data;
1194 if (stream->candidates_prepared == FALSE) {
1195 g_list_free(streams);
1196 prepared = FALSE;
1197 break;
1201 return prepared;
1202 #else
1203 return FALSE;
1204 #endif
1207 gboolean
1208 purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec)
1210 #ifdef USE_VV
1211 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1213 return purple_media_backend_set_send_codec(
1214 media->priv->backend, sess_id, codec);
1215 #else
1216 return FALSE;
1217 #endif
1220 gboolean
1221 purple_media_set_encryption_parameters(PurpleMedia *media, const gchar *sess_id,
1222 const gchar *cipher, const gchar *auth,
1223 const gchar *key, gsize key_len)
1225 #ifdef USE_VV
1226 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1227 return purple_media_backend_set_encryption_parameters(media->priv->backend,
1228 sess_id, cipher, auth, key, key_len);
1229 #else
1230 return FALSE;
1231 #endif
1234 gboolean
1235 purple_media_set_decryption_parameters(PurpleMedia *media, const gchar *sess_id,
1236 const gchar *participant, const gchar *cipher,
1237 const gchar *auth, const gchar *key, gsize key_len)
1239 #ifdef USE_VV
1240 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1241 return purple_media_backend_set_decryption_parameters(media->priv->backend,
1242 sess_id, participant, cipher, auth, key, key_len);
1243 #else
1244 return FALSE;
1245 #endif
1248 gboolean
1249 purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id)
1251 #ifdef USE_VV
1252 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1254 return purple_media_backend_codecs_ready(
1255 media->priv->backend, sess_id);
1256 #else
1257 return FALSE;
1258 #endif
1261 gboolean
1262 purple_media_set_send_rtcp_mux(PurpleMedia *media, const gchar *sess_id,
1263 const gchar *participant, gboolean send_rtcp_mux)
1265 #ifdef USE_VV
1266 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1268 return purple_media_backend_set_send_rtcp_mux(media->priv->backend,
1269 sess_id, participant, send_rtcp_mux);
1270 #else
1271 return FALSE;
1272 #endif
1275 gboolean
1276 purple_media_is_initiator(PurpleMedia *media,
1277 const gchar *sess_id, const gchar *participant)
1279 #ifdef USE_VV
1280 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1282 if (sess_id == NULL && participant == NULL)
1283 return media->priv->initiator;
1284 else if (sess_id != NULL && participant == NULL) {
1285 PurpleMediaSession *session =
1286 purple_media_get_session(media, sess_id);
1287 return session != NULL ? session->initiator : FALSE;
1288 } else if (sess_id != NULL && participant != NULL) {
1289 PurpleMediaStream *stream = purple_media_get_stream(
1290 media, sess_id, participant);
1291 return stream != NULL ? stream->initiator : FALSE;
1293 #endif
1294 return FALSE;
1297 gboolean
1298 purple_media_accepted(PurpleMedia *media, const gchar *sess_id,
1299 const gchar *participant)
1301 #ifdef USE_VV
1302 gboolean accepted = TRUE;
1304 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1306 if (sess_id == NULL && participant == NULL) {
1307 GList *streams = media->priv->streams;
1309 for (; streams; streams = g_list_next(streams)) {
1310 PurpleMediaStream *stream = streams->data;
1311 if (stream->accepted == FALSE) {
1312 accepted = FALSE;
1313 break;
1316 } else if (sess_id != NULL && participant == NULL) {
1317 GList *streams = purple_media_get_streams(
1318 media, sess_id, NULL);
1319 for (; streams; streams =
1320 g_list_delete_link(streams, streams)) {
1321 PurpleMediaStream *stream = streams->data;
1322 if (stream->accepted == FALSE) {
1323 g_list_free(streams);
1324 accepted = FALSE;
1325 break;
1328 } else if (sess_id != NULL && participant != NULL) {
1329 PurpleMediaStream *stream = purple_media_get_stream(
1330 media, sess_id, participant);
1331 if (stream == NULL || stream->accepted == FALSE)
1332 accepted = FALSE;
1335 return accepted;
1336 #else
1337 return FALSE;
1338 #endif
1341 void purple_media_set_input_volume(PurpleMedia *media,
1342 const gchar *session_id, double level)
1344 #ifdef USE_VV
1345 g_return_if_fail(PURPLE_IS_MEDIA(media));
1346 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
1348 purple_media_backend_fs2_set_input_volume(
1349 PURPLE_MEDIA_BACKEND_FS2(
1350 media->priv->backend),
1351 session_id, level);
1352 #endif
1355 void purple_media_set_output_volume(PurpleMedia *media,
1356 const gchar *session_id, const gchar *participant,
1357 double level)
1359 #ifdef USE_VV
1360 g_return_if_fail(PURPLE_IS_MEDIA(media));
1361 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
1363 purple_media_backend_fs2_set_output_volume(
1364 PURPLE_MEDIA_BACKEND_FS2(
1365 media->priv->backend),
1366 session_id, participant, level);
1367 #endif
1370 gulong
1371 purple_media_set_output_window(PurpleMedia *media, const gchar *session_id,
1372 const gchar *participant, gulong window_id)
1374 #ifdef USE_VV
1375 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1377 return purple_media_manager_set_output_window(media->priv->manager,
1378 media, session_id, participant, window_id);
1379 #else
1380 return 0;
1381 #endif
1384 void
1385 purple_media_remove_output_windows(PurpleMedia *media)
1387 #ifdef USE_VV
1388 GList *iter = media->priv->streams;
1389 for (; iter; iter = g_list_next(iter)) {
1390 PurpleMediaStream *stream = iter->data;
1391 purple_media_manager_remove_output_windows(
1392 media->priv->manager, media,
1393 stream->session->id, stream->participant);
1396 iter = purple_media_get_session_ids(media);
1397 for (; iter; iter = g_list_delete_link(iter, iter)) {
1398 gchar *session_name = iter->data;
1399 purple_media_manager_remove_output_windows(
1400 media->priv->manager, media,
1401 session_name, NULL);
1403 #endif
1406 #ifdef USE_VV
1407 GstElement *
1408 purple_media_get_tee(PurpleMedia *media,
1409 const gchar *session_id, const gchar *participant)
1411 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1413 if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
1414 return purple_media_backend_fs2_get_tee(
1415 PURPLE_MEDIA_BACKEND_FS2(
1416 media->priv->backend),
1417 session_id, participant);
1418 g_return_val_if_reached(NULL);
1420 #endif /* USE_VV */
1422 gboolean
1423 purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id,
1424 gchar dtmf, guint8 volume, guint16 duration)
1426 #ifdef USE_VV
1427 PurpleMediaBackendInterface *backend_iface = NULL;
1429 if (media)
1431 backend_iface = PURPLE_MEDIA_BACKEND_GET_INTERFACE(media->priv->backend);
1434 if (dtmf == 'a')
1435 dtmf = 'A';
1436 else if (dtmf == 'b')
1437 dtmf = 'B';
1438 else if (dtmf == 'c')
1439 dtmf = 'C';
1440 else if (dtmf == 'd')
1441 dtmf = 'D';
1443 g_return_val_if_fail(strchr("0123456789ABCD#*", dtmf), FALSE);
1445 if (backend_iface && backend_iface->send_dtmf
1446 && backend_iface->send_dtmf(media->priv->backend,
1447 session_id, dtmf, volume, duration))
1449 return TRUE;
1451 #endif
1452 return FALSE;