Updated Bengali translation, a month and a half after it was submitted. Fixes #12141.
[pidgin-git.git] / libpurple / media.c
blob0bf35ca310c3b2d72c574bfef919d8bffb1463ef
1 /**
2 * @file media.c Media API
3 * @ingroup core
4 */
6 /* purple
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 #include "internal.h"
28 #include "account.h"
29 #include "media.h"
30 #include "media/backend-iface.h"
31 #include "mediamanager.h"
33 #include "debug.h"
35 #ifdef USE_GSTREAMER
36 #include "media/backend-fs2.h"
37 #include "marshallers.h"
38 #include "media-gst.h"
39 #endif
41 #ifdef USE_VV
43 /** @copydoc _PurpleMediaSession */
44 typedef struct _PurpleMediaSession PurpleMediaSession;
45 /** @copydoc _PurpleMediaStream */
46 typedef struct _PurpleMediaStream PurpleMediaStream;
47 /** @copydoc _PurpleMediaClass */
48 typedef struct _PurpleMediaClass PurpleMediaClass;
49 /** @copydoc _PurpleMediaPrivate */
50 typedef struct _PurpleMediaPrivate PurpleMediaPrivate;
52 /** The media class */
53 struct _PurpleMediaClass
55 GObjectClass parent_class; /**< The parent class. */
58 /** The media class's private data */
59 struct _PurpleMedia
61 GObject parent; /**< The parent of this object. */
62 PurpleMediaPrivate *priv; /**< The private data of this object. */
65 struct _PurpleMediaSession
67 gchar *id;
68 PurpleMedia *media;
69 PurpleMediaSessionType type;
70 gboolean initiator;
73 struct _PurpleMediaStream
75 PurpleMediaSession *session;
76 gchar *participant;
78 GList *local_candidates;
79 GList *remote_candidates;
81 gboolean initiator;
82 gboolean accepted;
83 gboolean candidates_prepared;
85 GList *active_local_candidates;
86 GList *active_remote_candidates;
88 #endif
90 struct _PurpleMediaPrivate
92 #ifdef USE_VV
93 PurpleMediaManager *manager;
94 PurpleAccount *account;
95 PurpleMediaBackend *backend;
96 gchar *conference_type;
97 gboolean initiator;
98 gpointer prpl_data;
100 GHashTable *sessions; /* PurpleMediaSession table */
101 GList *participants;
102 GList *streams; /* PurpleMediaStream table */
103 #else
104 gpointer dummy;
105 #endif
108 #ifdef USE_VV
109 #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate))
111 static void purple_media_class_init (PurpleMediaClass *klass);
112 static void purple_media_init (PurpleMedia *media);
113 static void purple_media_dispose (GObject *object);
114 static void purple_media_finalize (GObject *object);
115 static void purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
116 static void purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
118 static void purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
119 const gchar *sess_id, const gchar *participant,
120 PurpleMediaCandidate *candidate, PurpleMedia *media);
121 static void purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
122 const gchar *sess_id, const gchar *name, PurpleMedia *media);
123 static void purple_media_candidate_pair_established_cb(
124 PurpleMediaBackend *backend,
125 const gchar *sess_id, const gchar *name,
126 PurpleMediaCandidate *local_candidate,
127 PurpleMediaCandidate *remote_candidate,
128 PurpleMedia *media);
129 static void purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
130 const gchar *sess_id, PurpleMedia *media);
132 static GObjectClass *parent_class = NULL;
136 enum {
137 S_ERROR,
138 CANDIDATES_PREPARED,
139 CODECS_CHANGED,
140 LEVEL,
141 NEW_CANDIDATE,
142 STATE_CHANGED,
143 STREAM_INFO,
144 LAST_SIGNAL
146 static guint purple_media_signals[LAST_SIGNAL] = {0};
148 enum {
149 PROP_0,
150 PROP_MANAGER,
151 PROP_BACKEND,
152 PROP_ACCOUNT,
153 PROP_CONFERENCE_TYPE,
154 PROP_INITIATOR,
155 PROP_PRPL_DATA,
157 #endif
160 GType
161 purple_media_get_type()
163 #ifdef USE_VV
164 static GType type = 0;
166 if (type == 0) {
167 static const GTypeInfo info = {
168 sizeof(PurpleMediaClass),
169 NULL,
170 NULL,
171 (GClassInitFunc) purple_media_class_init,
172 NULL,
173 NULL,
174 sizeof(PurpleMedia),
176 (GInstanceInitFunc) purple_media_init,
177 NULL
179 type = g_type_register_static(G_TYPE_OBJECT, "PurpleMedia", &info, 0);
181 return type;
182 #else
183 return G_TYPE_NONE;
184 #endif
187 #ifdef USE_VV
188 static void
189 purple_media_class_init (PurpleMediaClass *klass)
191 GObjectClass *gobject_class = (GObjectClass*)klass;
192 parent_class = g_type_class_peek_parent(klass);
194 gobject_class->dispose = purple_media_dispose;
195 gobject_class->finalize = purple_media_finalize;
196 gobject_class->set_property = purple_media_set_property;
197 gobject_class->get_property = purple_media_get_property;
199 g_object_class_install_property(gobject_class, PROP_MANAGER,
200 g_param_spec_object("manager",
201 "Purple Media Manager",
202 "The media manager that contains this media session.",
203 PURPLE_TYPE_MEDIA_MANAGER,
204 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
207 * This one should be PURPLE_TYPE_MEDIA_BACKEND, but it doesn't
208 * like interfaces because they "aren't GObjects"
210 g_object_class_install_property(gobject_class, PROP_BACKEND,
211 g_param_spec_object("backend",
212 "Purple Media Backend",
213 "The backend object this media object uses.",
214 G_TYPE_OBJECT,
215 G_PARAM_READABLE));
217 g_object_class_install_property(gobject_class, PROP_ACCOUNT,
218 g_param_spec_pointer("account",
219 "PurpleAccount",
220 "The account this media session is on.",
221 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
223 g_object_class_install_property(gobject_class, PROP_CONFERENCE_TYPE,
224 g_param_spec_string("conference-type",
225 "Conference Type",
226 "The type of conference that this media object "
227 "has been created to provide.",
228 NULL,
229 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
231 g_object_class_install_property(gobject_class, PROP_INITIATOR,
232 g_param_spec_boolean("initiator",
233 "initiator",
234 "If the local user initiated the conference.",
235 FALSE,
236 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
238 g_object_class_install_property(gobject_class, PROP_PRPL_DATA,
239 g_param_spec_pointer("prpl-data",
240 "gpointer",
241 "Data the prpl plugin set on the media session.",
242 G_PARAM_READWRITE));
244 purple_media_signals[S_ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(klass),
245 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
246 g_cclosure_marshal_VOID__STRING,
247 G_TYPE_NONE, 1, G_TYPE_STRING);
248 purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass),
249 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
250 purple_smarshal_VOID__STRING_STRING,
251 G_TYPE_NONE, 2, G_TYPE_STRING,
252 G_TYPE_STRING);
253 purple_media_signals[CODECS_CHANGED] = g_signal_new("codecs-changed", G_TYPE_FROM_CLASS(klass),
254 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
255 g_cclosure_marshal_VOID__STRING,
256 G_TYPE_NONE, 1, G_TYPE_STRING);
257 purple_media_signals[LEVEL] = g_signal_new("level", G_TYPE_FROM_CLASS(klass),
258 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
259 purple_smarshal_VOID__STRING_STRING_DOUBLE,
260 G_TYPE_NONE, 3, G_TYPE_STRING,
261 G_TYPE_STRING, G_TYPE_DOUBLE);
262 purple_media_signals[NEW_CANDIDATE] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass),
263 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
264 purple_smarshal_VOID__POINTER_POINTER_OBJECT,
265 G_TYPE_NONE, 3, G_TYPE_POINTER,
266 G_TYPE_POINTER, PURPLE_TYPE_MEDIA_CANDIDATE);
267 purple_media_signals[STATE_CHANGED] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass),
268 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
269 purple_smarshal_VOID__ENUM_STRING_STRING,
270 G_TYPE_NONE, 3, PURPLE_MEDIA_TYPE_STATE,
271 G_TYPE_STRING, G_TYPE_STRING);
272 purple_media_signals[STREAM_INFO] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass),
273 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
274 purple_smarshal_VOID__ENUM_STRING_STRING_BOOLEAN,
275 G_TYPE_NONE, 4, PURPLE_MEDIA_TYPE_INFO_TYPE,
276 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
277 g_type_class_add_private(klass, sizeof(PurpleMediaPrivate));
281 static void
282 purple_media_init (PurpleMedia *media)
284 media->priv = PURPLE_MEDIA_GET_PRIVATE(media);
285 memset(media->priv, 0, sizeof(*media->priv));
288 static void
289 purple_media_stream_free(PurpleMediaStream *stream)
291 if (stream == NULL)
292 return;
294 g_free(stream->participant);
296 if (stream->local_candidates)
297 purple_media_candidate_list_free(stream->local_candidates);
298 if (stream->remote_candidates)
299 purple_media_candidate_list_free(stream->remote_candidates);
301 if (stream->active_local_candidates)
302 purple_media_candidate_list_free(
303 stream->active_local_candidates);
304 if (stream->active_remote_candidates)
305 purple_media_candidate_list_free(
306 stream->active_remote_candidates);
308 g_free(stream);
311 static void
312 purple_media_session_free(PurpleMediaSession *session)
314 if (session == NULL)
315 return;
317 g_free(session->id);
318 g_free(session);
321 static void
322 purple_media_dispose(GObject *media)
324 PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
326 purple_debug_info("media","purple_media_dispose\n");
328 purple_media_manager_remove_media(priv->manager, PURPLE_MEDIA(media));
330 if (priv->backend) {
331 g_object_unref(priv->backend);
332 priv->backend = NULL;
335 if (priv->manager) {
336 g_object_unref(priv->manager);
337 priv->manager = NULL;
340 G_OBJECT_CLASS(parent_class)->dispose(media);
343 static void
344 purple_media_finalize(GObject *media)
346 PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
347 purple_debug_info("media","purple_media_finalize\n");
349 for (; priv->streams; priv->streams = g_list_delete_link(priv->streams, priv->streams))
350 purple_media_stream_free(priv->streams->data);
352 for (; priv->participants; priv->participants = g_list_delete_link(
353 priv->participants, priv->participants))
354 g_free(priv->participants->data);
356 if (priv->sessions) {
357 GList *sessions = g_hash_table_get_values(priv->sessions);
358 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
359 purple_media_session_free(sessions->data);
361 g_hash_table_destroy(priv->sessions);
364 G_OBJECT_CLASS(parent_class)->finalize(media);
367 static void
368 purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
370 PurpleMedia *media;
371 g_return_if_fail(PURPLE_IS_MEDIA(object));
373 media = PURPLE_MEDIA(object);
375 switch (prop_id) {
376 case PROP_MANAGER:
377 media->priv->manager = g_value_dup_object(value);
378 break;
379 case PROP_ACCOUNT:
380 media->priv->account = g_value_get_pointer(value);
381 break;
382 case PROP_CONFERENCE_TYPE:
383 media->priv->conference_type =
384 g_value_dup_string(value);
385 media->priv->backend = g_object_new(
386 purple_media_manager_get_backend_type(
387 purple_media_manager_get()),
388 "conference-type",
389 media->priv->conference_type,
390 "media", media,
391 NULL);
392 g_signal_connect(media->priv->backend,
393 "active-candidate-pair",
394 G_CALLBACK(
395 purple_media_candidate_pair_established_cb),
396 media);
397 g_signal_connect(media->priv->backend,
398 "candidates-prepared",
399 G_CALLBACK(
400 purple_media_candidates_prepared_cb),
401 media);
402 g_signal_connect(media->priv->backend,
403 "codecs-changed",
404 G_CALLBACK(
405 purple_media_codecs_changed_cb),
406 media);
407 g_signal_connect(media->priv->backend,
408 "new-candidate",
409 G_CALLBACK(
410 purple_media_new_local_candidate_cb),
411 media);
412 break;
413 case PROP_INITIATOR:
414 media->priv->initiator = g_value_get_boolean(value);
415 break;
416 case PROP_PRPL_DATA:
417 media->priv->prpl_data = g_value_get_pointer(value);
418 break;
419 default:
420 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
421 break;
425 static void
426 purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
428 PurpleMedia *media;
429 g_return_if_fail(PURPLE_IS_MEDIA(object));
431 media = PURPLE_MEDIA(object);
433 switch (prop_id) {
434 case PROP_MANAGER:
435 g_value_set_object(value, media->priv->manager);
436 break;
437 case PROP_BACKEND:
438 g_value_set_object(value, media->priv->backend);
439 break;
440 case PROP_ACCOUNT:
441 g_value_set_pointer(value, media->priv->account);
442 break;
443 case PROP_CONFERENCE_TYPE:
444 g_value_set_string(value,
445 media->priv->conference_type);
446 break;
447 case PROP_INITIATOR:
448 g_value_set_boolean(value, media->priv->initiator);
449 break;
450 case PROP_PRPL_DATA:
451 g_value_set_pointer(value, media->priv->prpl_data);
452 break;
453 default:
454 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
455 break;
460 static PurpleMediaSession*
461 purple_media_get_session(PurpleMedia *media, const gchar *sess_id)
463 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
464 return (PurpleMediaSession*) (media->priv->sessions) ?
465 g_hash_table_lookup(media->priv->sessions, sess_id) : NULL;
468 static PurpleMediaStream*
469 purple_media_get_stream(PurpleMedia *media, const gchar *session, const gchar *participant)
471 GList *streams;
473 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
475 streams = media->priv->streams;
477 for (; streams; streams = g_list_next(streams)) {
478 PurpleMediaStream *stream = streams->data;
479 if (!strcmp(stream->session->id, session) &&
480 !strcmp(stream->participant, participant))
481 return stream;
484 return NULL;
487 static GList *
488 purple_media_get_streams(PurpleMedia *media, const gchar *session,
489 const gchar *participant)
491 GList *streams;
492 GList *ret = NULL;
494 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
496 streams = media->priv->streams;
498 for (; streams; streams = g_list_next(streams)) {
499 PurpleMediaStream *stream = streams->data;
500 if ((session == NULL ||
501 !strcmp(stream->session->id, session)) &&
502 (participant == NULL ||
503 !strcmp(stream->participant, participant)))
504 ret = g_list_append(ret, stream);
507 return ret;
510 static void
511 purple_media_add_session(PurpleMedia *media, PurpleMediaSession *session)
513 g_return_if_fail(PURPLE_IS_MEDIA(media));
514 g_return_if_fail(session != NULL);
516 if (!media->priv->sessions) {
517 purple_debug_info("media", "Creating hash table for sessions\n");
518 media->priv->sessions = g_hash_table_new(g_str_hash, g_str_equal);
520 g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session);
523 #if 0
524 static gboolean
525 purple_media_remove_session(PurpleMedia *media, PurpleMediaSession *session)
527 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
528 return g_hash_table_remove(media->priv->sessions, session->id);
530 #endif
532 static PurpleMediaStream *
533 purple_media_insert_stream(PurpleMediaSession *session,
534 const gchar *name, gboolean initiator)
536 PurpleMediaStream *media_stream;
538 g_return_val_if_fail(session != NULL, NULL);
540 media_stream = g_new0(PurpleMediaStream, 1);
541 media_stream->participant = g_strdup(name);
542 media_stream->session = session;
543 media_stream->initiator = initiator;
545 session->media->priv->streams =
546 g_list_append(session->media->priv->streams, media_stream);
548 return media_stream;
551 static void
552 purple_media_insert_local_candidate(PurpleMediaSession *session, const gchar *name,
553 PurpleMediaCandidate *candidate)
555 PurpleMediaStream *stream;
557 g_return_if_fail(session != NULL);
559 stream = purple_media_get_stream(session->media, session->id, name);
560 stream->local_candidates = g_list_append(stream->local_candidates, candidate);
562 #endif
564 GList *
565 purple_media_get_session_ids(PurpleMedia *media)
567 #ifdef USE_VV
568 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
569 return media->priv->sessions != NULL ?
570 g_hash_table_get_keys(media->priv->sessions) : NULL;
571 #else
572 return NULL;
573 #endif
576 #ifdef USE_GSTREAMER
577 GstElement *
578 purple_media_get_src(PurpleMedia *media, const gchar *sess_id)
580 #ifdef USE_VV
581 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
583 if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
584 return purple_media_backend_fs2_get_src(
585 PURPLE_MEDIA_BACKEND_FS2(
586 media->priv->backend), sess_id);
588 g_return_val_if_reached(NULL);
589 #else
590 return NULL;
591 #endif
593 #endif /* USE_GSTREAMER */
595 PurpleAccount *
596 purple_media_get_account(PurpleMedia *media)
598 #ifdef USE_VV
599 PurpleAccount *account;
600 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
601 g_object_get(G_OBJECT(media), "account", &account, NULL);
602 return account;
603 #else
604 return NULL;
605 #endif
608 gpointer
609 purple_media_get_prpl_data(PurpleMedia *media)
611 #ifdef USE_VV
612 gpointer prpl_data;
613 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
614 g_object_get(G_OBJECT(media), "prpl-data", &prpl_data, NULL);
615 return prpl_data;
616 #else
617 return NULL;
618 #endif
621 void
622 purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data)
624 #ifdef USE_VV
625 g_return_if_fail(PURPLE_IS_MEDIA(media));
626 g_object_set(G_OBJECT(media), "prpl-data", prpl_data, NULL);
627 #endif
630 void
631 purple_media_error(PurpleMedia *media, const gchar *error, ...)
633 #ifdef USE_VV
634 va_list args;
635 gchar *message;
637 g_return_if_fail(PURPLE_IS_MEDIA(media));
639 va_start(args, error);
640 message = g_strdup_vprintf(error, args);
641 va_end(args);
643 purple_debug_error("media", "%s\n", message);
644 g_signal_emit(media, purple_media_signals[S_ERROR], 0, message);
646 g_free(message);
647 #endif
650 void
651 purple_media_end(PurpleMedia *media,
652 const gchar *session_id, const gchar *participant)
654 #ifdef USE_VV
655 GList *iter, *sessions = NULL, *participants = NULL;
657 g_return_if_fail(PURPLE_IS_MEDIA(media));
659 iter = purple_media_get_streams(media, session_id, participant);
661 /* Free matching streams */
662 for (; iter; iter = g_list_delete_link(iter, iter)) {
663 PurpleMediaStream *stream = iter->data;
665 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
666 0, PURPLE_MEDIA_STATE_END,
667 stream->session->id, stream->participant);
669 media->priv->streams =
670 g_list_remove(media->priv->streams, stream);
672 if (g_list_find(sessions, stream->session) == NULL)
673 sessions = g_list_prepend(sessions, stream->session);
675 if (g_list_find_custom(participants, stream->participant,
676 (GCompareFunc)strcmp) == NULL)
677 participants = g_list_prepend(participants,
678 g_strdup(stream->participant));
680 purple_media_stream_free(stream);
683 iter = media->priv->streams;
685 /* Reduce to list of sessions to remove */
686 for (; iter; iter = g_list_next(iter)) {
687 PurpleMediaStream *stream = iter->data;
689 sessions = g_list_remove(sessions, stream->session);
692 /* Free sessions with no streams left */
693 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
694 PurpleMediaSession *session = sessions->data;
696 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
697 0, PURPLE_MEDIA_STATE_END,
698 session->id, NULL);
700 g_hash_table_remove(media->priv->sessions, session->id);
701 purple_media_session_free(session);
704 iter = media->priv->streams;
706 /* Reduce to list of participants to remove */
707 for (; iter; iter = g_list_next(iter)) {
708 PurpleMediaStream *stream = iter->data;
709 GList *tmp;
711 tmp = g_list_find_custom(participants,
712 stream->participant, (GCompareFunc)strcmp);
714 if (tmp != NULL) {
715 g_free(tmp->data);
716 participants = g_list_delete_link(participants, tmp);
720 /* Remove participants with no streams left (just emit the signal) */
721 for (; participants; participants =
722 g_list_delete_link(participants, participants)) {
723 gchar *participant = participants->data;
724 GList *link = g_list_find_custom(media->priv->participants,
725 participant, (GCompareFunc)strcmp);
727 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
728 0, PURPLE_MEDIA_STATE_END,
729 NULL, participant);
731 if (link != NULL) {
732 g_free(link->data);
733 media->priv->participants = g_list_delete_link(
734 media->priv->participants, link);
737 g_free(participant);
740 /* Free the conference if no sessions left */
741 if (media->priv->sessions != NULL &&
742 g_hash_table_size(media->priv->sessions) == 0) {
743 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
744 0, PURPLE_MEDIA_STATE_END,
745 NULL, NULL);
746 g_object_unref(media);
747 return;
749 #endif
752 void
753 purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
754 const gchar *session_id, const gchar *participant,
755 gboolean local)
757 #ifdef USE_VV
758 g_return_if_fail(PURPLE_IS_MEDIA(media));
760 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
761 GList *streams, *sessions = NULL, *participants = NULL;
763 g_return_if_fail(PURPLE_IS_MEDIA(media));
765 streams = purple_media_get_streams(media,
766 session_id, participant);
768 /* Emit stream acceptance */
769 for (; streams; streams =
770 g_list_delete_link(streams, streams)) {
771 PurpleMediaStream *stream = streams->data;
773 stream->accepted = TRUE;
775 g_signal_emit(media,
776 purple_media_signals[STREAM_INFO],
777 0, type, stream->session->id,
778 stream->participant, local);
780 if (g_list_find(sessions, stream->session) == NULL)
781 sessions = g_list_prepend(sessions,
782 stream->session);
784 if (g_list_find_custom(participants,
785 stream->participant,
786 (GCompareFunc)strcmp) == NULL)
787 participants = g_list_prepend(participants,
788 g_strdup(stream->participant));
791 /* Emit session acceptance */
792 for (; sessions; sessions =
793 g_list_delete_link(sessions, sessions)) {
794 PurpleMediaSession *session = sessions->data;
796 if (purple_media_accepted(media, session->id, NULL))
797 g_signal_emit(media, purple_media_signals[
798 STREAM_INFO], 0,
799 PURPLE_MEDIA_INFO_ACCEPT,
800 session->id, NULL, local);
803 /* Emit participant acceptance */
804 for (; participants; participants = g_list_delete_link(
805 participants, participants)) {
806 gchar *participant = participants->data;
808 if (purple_media_accepted(media, NULL, participant))
809 g_signal_emit(media, purple_media_signals[
810 STREAM_INFO], 0,
811 PURPLE_MEDIA_INFO_ACCEPT,
812 NULL, participant, local);
814 g_free(participant);
817 /* Emit conference acceptance */
818 if (purple_media_accepted(media, NULL, NULL))
819 g_signal_emit(media,
820 purple_media_signals[STREAM_INFO],
821 0, PURPLE_MEDIA_INFO_ACCEPT,
822 NULL, NULL, local);
824 return;
825 } else if (type == PURPLE_MEDIA_INFO_HANGUP ||
826 type == PURPLE_MEDIA_INFO_REJECT) {
827 GList *streams;
829 g_return_if_fail(PURPLE_IS_MEDIA(media));
831 streams = purple_media_get_streams(media,
832 session_id, participant);
834 /* Emit for stream */
835 for (; streams; streams =
836 g_list_delete_link(streams, streams)) {
837 PurpleMediaStream *stream = streams->data;
839 g_signal_emit(media,
840 purple_media_signals[STREAM_INFO],
841 0, type, stream->session->id,
842 stream->participant, local);
845 if (session_id != NULL && participant != NULL) {
846 /* Everything that needs to be emitted has been */
847 } else if (session_id == NULL && participant == NULL) {
848 /* Emit for everything in the conference */
849 GList *sessions = NULL;
850 GList *participants = media->priv->participants;
852 if (media->priv->sessions != NULL)
853 sessions = g_hash_table_get_values(
854 media->priv->sessions);
856 /* Emit for sessions */
857 for (; sessions; sessions = g_list_delete_link(
858 sessions, sessions)) {
859 PurpleMediaSession *session = sessions->data;
861 g_signal_emit(media, purple_media_signals[
862 STREAM_INFO], 0, type,
863 session->id, NULL, local);
866 /* Emit for participants */
867 for (; participants; participants =
868 g_list_next(participants)) {
869 gchar *participant = participants->data;
871 g_signal_emit(media, purple_media_signals[
872 STREAM_INFO], 0, type,
873 NULL, participant, local);
876 /* Emit for conference */
877 g_signal_emit(media,
878 purple_media_signals[STREAM_INFO],
879 0, type, NULL, NULL, local);
880 } else if (session_id != NULL) {
881 /* Emit just the specific session */
882 PurpleMediaSession *session =
883 purple_media_get_session(
884 media, session_id);
886 if (session == NULL) {
887 purple_debug_warning("media",
888 "Couldn't find session"
889 " to hangup/reject.\n");
890 } else {
891 g_signal_emit(media, purple_media_signals[
892 STREAM_INFO], 0, type,
893 session->id, NULL, local);
895 } else if (participant != NULL) {
896 /* Emit just the specific participant */
897 if (!g_list_find_custom(media->priv->participants,
898 participant, (GCompareFunc)strcmp)) {
899 purple_debug_warning("media",
900 "Couldn't find participant"
901 " to hangup/reject.\n");
902 } else {
903 g_signal_emit(media, purple_media_signals[
904 STREAM_INFO], 0, type, NULL,
905 participant, local);
909 purple_media_end(media, session_id, participant);
910 return;
913 g_signal_emit(media, purple_media_signals[STREAM_INFO],
914 0, type, session_id, participant, local);
915 #endif
918 #ifdef USE_VV
919 static void
920 purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
921 const gchar *sess_id, const gchar *participant,
922 PurpleMediaCandidate *candidate, PurpleMedia *media)
924 PurpleMediaSession *session =
925 purple_media_get_session(media, sess_id);
927 purple_media_insert_local_candidate(session, participant,
928 purple_media_candidate_copy(candidate));
930 g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE],
931 0, session->id, participant, candidate);
934 static void
935 purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
936 const gchar *sess_id, const gchar *name, PurpleMedia *media)
938 PurpleMediaStream *stream_data;
940 g_return_if_fail(PURPLE_IS_MEDIA(media));
942 stream_data = purple_media_get_stream(media, sess_id, name);
943 stream_data->candidates_prepared = TRUE;
945 g_signal_emit(media, purple_media_signals[CANDIDATES_PREPARED],
946 0, sess_id, name);
949 /* callback called when a pair of transport candidates (local and remote)
950 * has been established */
951 static void
952 purple_media_candidate_pair_established_cb(PurpleMediaBackend *backend,
953 const gchar *sess_id, const gchar *name,
954 PurpleMediaCandidate *local_candidate,
955 PurpleMediaCandidate *remote_candidate,
956 PurpleMedia *media)
958 PurpleMediaStream *stream;
959 GList *iter;
960 guint id;
962 g_return_if_fail(PURPLE_IS_MEDIA(media));
964 stream = purple_media_get_stream(media, sess_id, name);
965 id = purple_media_candidate_get_component_id(local_candidate);
967 iter = stream->active_local_candidates;
968 for(; iter; iter = g_list_next(iter)) {
969 PurpleMediaCandidate *c = iter->data;
970 if (id == purple_media_candidate_get_component_id(c)) {
971 g_object_unref(c);
972 stream->active_local_candidates =
973 g_list_delete_link(iter, iter);
974 stream->active_local_candidates = g_list_prepend(
975 stream->active_local_candidates,
976 purple_media_candidate_copy(
977 local_candidate));
978 break;
981 if (iter == NULL)
982 stream->active_local_candidates = g_list_prepend(
983 stream->active_local_candidates,
984 purple_media_candidate_copy(
985 local_candidate));
987 id = purple_media_candidate_get_component_id(local_candidate);
989 iter = stream->active_remote_candidates;
990 for(; iter; iter = g_list_next(iter)) {
991 PurpleMediaCandidate *c = iter->data;
992 if (id == purple_media_candidate_get_component_id(c)) {
993 g_object_unref(c);
994 stream->active_remote_candidates =
995 g_list_delete_link(iter, iter);
996 stream->active_remote_candidates = g_list_prepend(
997 stream->active_remote_candidates,
998 purple_media_candidate_copy(
999 remote_candidate));
1000 break;
1003 if (iter == NULL)
1004 stream->active_remote_candidates = g_list_prepend(
1005 stream->active_remote_candidates,
1006 purple_media_candidate_copy(
1007 remote_candidate));
1009 purple_debug_info("media", "candidate pair established\n");
1012 static void
1013 purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
1014 const gchar *sess_id, PurpleMedia *media)
1016 g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, sess_id);
1018 #endif /* USE_VV */
1020 gboolean
1021 purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
1022 const gchar *who, PurpleMediaSessionType type,
1023 gboolean initiator, const gchar *transmitter,
1024 guint num_params, GParameter *params)
1026 #ifdef USE_VV
1027 PurpleMediaSession *session;
1028 PurpleMediaStream *stream = NULL;
1030 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1032 if (!purple_media_backend_add_stream(media->priv->backend,
1033 sess_id, who, type, initiator, transmitter,
1034 num_params, params)) {
1035 purple_debug_error("media", "Error adding stream.\n");
1036 return FALSE;
1039 session = purple_media_get_session(media, sess_id);
1041 if (!session) {
1042 session = g_new0(PurpleMediaSession, 1);
1043 session->id = g_strdup(sess_id);
1044 session->media = media;
1045 session->type = type;
1046 session->initiator = initiator;
1048 purple_media_add_session(media, session);
1049 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
1050 0, PURPLE_MEDIA_STATE_NEW,
1051 session->id, NULL);
1054 if (!g_list_find_custom(media->priv->participants,
1055 who, (GCompareFunc)strcmp)) {
1056 media->priv->participants = g_list_prepend(
1057 media->priv->participants, g_strdup(who));
1059 g_signal_emit_by_name(media, "state-changed",
1060 PURPLE_MEDIA_STATE_NEW, NULL, who);
1063 if (purple_media_get_stream(media, sess_id, who) == NULL) {
1064 stream = purple_media_insert_stream(session, who, initiator);
1066 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
1067 0, PURPLE_MEDIA_STATE_NEW,
1068 session->id, who);
1071 return TRUE;
1072 #else
1073 return FALSE;
1074 #endif /* USE_VV */
1077 PurpleMediaManager *
1078 purple_media_get_manager(PurpleMedia *media)
1080 PurpleMediaManager *ret;
1081 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1082 g_object_get(media, "manager", &ret, NULL);
1083 return ret;
1086 PurpleMediaSessionType
1087 purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id)
1089 #ifdef USE_VV
1090 PurpleMediaSession *session;
1091 g_return_val_if_fail(PURPLE_IS_MEDIA(media), PURPLE_MEDIA_NONE);
1092 session = purple_media_get_session(media, sess_id);
1093 return session->type;
1094 #else
1095 return PURPLE_MEDIA_NONE;
1096 #endif
1098 /* XXX: Should wait until codecs-ready is TRUE before using this function */
1099 GList *
1100 purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id)
1102 #ifdef USE_VV
1103 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1105 return purple_media_backend_get_codecs(media->priv->backend, sess_id);
1106 #else
1107 return NULL;
1108 #endif
1111 GList *
1112 purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id,
1113 const gchar *participant)
1115 #ifdef USE_VV
1116 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1118 return purple_media_backend_get_local_candidates(media->priv->backend,
1119 sess_id, participant);
1120 #else
1121 return NULL;
1122 #endif
1125 void
1126 purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id,
1127 const gchar *participant,
1128 GList *remote_candidates)
1130 #ifdef USE_VV
1131 PurpleMediaStream *stream;
1133 g_return_if_fail(PURPLE_IS_MEDIA(media));
1134 stream = purple_media_get_stream(media, sess_id, participant);
1136 if (stream == NULL) {
1137 purple_debug_error("media",
1138 "purple_media_add_remote_candidates: "
1139 "couldn't find stream %s %s.\n",
1140 sess_id ? sess_id : "(null)",
1141 participant ? participant : "(null)");
1142 return;
1145 stream->remote_candidates = g_list_concat(stream->remote_candidates,
1146 purple_media_candidate_list_copy(remote_candidates));
1148 purple_media_backend_add_remote_candidates(media->priv->backend,
1149 sess_id, participant, remote_candidates);
1150 #endif
1153 #if 0
1155 * These two functions aren't being used and I'd rather not lock in the API
1156 * until they are needed. If they ever are.
1159 GList *
1160 purple_media_get_active_local_candidates(PurpleMedia *media,
1161 const gchar *sess_id, const gchar *participant)
1163 #ifdef USE_VV
1164 PurpleMediaStream *stream;
1165 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1166 stream = purple_media_get_stream(media, sess_id, participant);
1167 return purple_media_candidate_list_copy(
1168 stream->active_local_candidates);
1169 #else
1170 return NULL;
1171 #endif
1174 GList *
1175 purple_media_get_active_remote_candidates(PurpleMedia *media,
1176 const gchar *sess_id, const gchar *participant)
1178 #ifdef USE_VV
1179 PurpleMediaStream *stream;
1180 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1181 stream = purple_media_get_stream(media, sess_id, participant);
1182 return purple_media_candidate_list_copy(
1183 stream->active_remote_candidates);
1184 #else
1185 return NULL;
1186 #endif
1188 #endif
1190 gboolean
1191 purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
1192 const gchar *participant, GList *codecs)
1194 #ifdef USE_VV
1195 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1197 return purple_media_backend_set_remote_codecs(media->priv->backend,
1198 sess_id, participant, codecs);
1199 #else
1200 return FALSE;
1201 #endif
1204 gboolean
1205 purple_media_candidates_prepared(PurpleMedia *media,
1206 const gchar *session_id, const gchar *participant)
1208 #ifdef USE_VV
1209 GList *streams;
1210 gboolean prepared = TRUE;
1212 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1214 streams = purple_media_get_streams(media, session_id, participant);
1216 for (; streams; streams = g_list_delete_link(streams, streams)) {
1217 PurpleMediaStream *stream = streams->data;
1218 if (stream->candidates_prepared == FALSE) {
1219 g_list_free(streams);
1220 prepared = FALSE;
1221 break;
1225 return prepared;
1226 #else
1227 return FALSE;
1228 #endif
1231 gboolean
1232 purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec)
1234 #ifdef USE_VV
1235 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1237 return purple_media_backend_set_send_codec(
1238 media->priv->backend, sess_id, codec);
1239 #else
1240 return FALSE;
1241 #endif
1244 gboolean
1245 purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id)
1247 #ifdef USE_VV
1248 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1250 return purple_media_backend_codecs_ready(
1251 media->priv->backend, sess_id);
1252 #else
1253 return FALSE;
1254 #endif
1257 gboolean
1258 purple_media_is_initiator(PurpleMedia *media,
1259 const gchar *sess_id, const gchar *participant)
1261 #ifdef USE_VV
1262 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1264 if (sess_id == NULL && participant == NULL)
1265 return media->priv->initiator;
1266 else if (sess_id != NULL && participant == NULL) {
1267 PurpleMediaSession *session =
1268 purple_media_get_session(media, sess_id);
1269 return session != NULL ? session->initiator : FALSE;
1270 } else if (sess_id != NULL && participant != NULL) {
1271 PurpleMediaStream *stream = purple_media_get_stream(
1272 media, sess_id, participant);
1273 return stream != NULL ? stream->initiator : FALSE;
1275 #endif
1276 return FALSE;
1279 gboolean
1280 purple_media_accepted(PurpleMedia *media, const gchar *sess_id,
1281 const gchar *participant)
1283 #ifdef USE_VV
1284 gboolean accepted = TRUE;
1286 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1288 if (sess_id == NULL && participant == NULL) {
1289 GList *streams = media->priv->streams;
1291 for (; streams; streams = g_list_next(streams)) {
1292 PurpleMediaStream *stream = streams->data;
1293 if (stream->accepted == FALSE) {
1294 accepted = FALSE;
1295 break;
1298 } else if (sess_id != NULL && participant == NULL) {
1299 GList *streams = purple_media_get_streams(
1300 media, sess_id, NULL);
1301 for (; streams; streams =
1302 g_list_delete_link(streams, streams)) {
1303 PurpleMediaStream *stream = streams->data;
1304 if (stream->accepted == FALSE) {
1305 g_list_free(streams);
1306 accepted = FALSE;
1307 break;
1310 } else if (sess_id != NULL && participant != NULL) {
1311 PurpleMediaStream *stream = purple_media_get_stream(
1312 media, sess_id, participant);
1313 if (stream == NULL || stream->accepted == FALSE)
1314 accepted = FALSE;
1317 return accepted;
1318 #else
1319 return FALSE;
1320 #endif
1323 void purple_media_set_input_volume(PurpleMedia *media,
1324 const gchar *session_id, double level)
1326 #ifdef USE_VV
1327 g_return_if_fail(PURPLE_IS_MEDIA(media));
1328 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
1330 purple_media_backend_fs2_set_input_volume(
1331 PURPLE_MEDIA_BACKEND_FS2(
1332 media->priv->backend),
1333 session_id, level);
1334 #endif
1337 void purple_media_set_output_volume(PurpleMedia *media,
1338 const gchar *session_id, const gchar *participant,
1339 double level)
1341 #ifdef USE_VV
1342 g_return_if_fail(PURPLE_IS_MEDIA(media));
1343 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
1345 purple_media_backend_fs2_set_output_volume(
1346 PURPLE_MEDIA_BACKEND_FS2(
1347 media->priv->backend),
1348 session_id, participant, level);
1349 #endif
1352 gulong
1353 purple_media_set_output_window(PurpleMedia *media, const gchar *session_id,
1354 const gchar *participant, gulong window_id)
1356 #ifdef USE_VV
1357 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1359 return purple_media_manager_set_output_window(media->priv->manager,
1360 media, session_id, participant, window_id);
1361 #else
1362 return 0;
1363 #endif
1366 void
1367 purple_media_remove_output_windows(PurpleMedia *media)
1369 #ifdef USE_VV
1370 GList *iter = media->priv->streams;
1371 for (; iter; iter = g_list_next(iter)) {
1372 PurpleMediaStream *stream = iter->data;
1373 purple_media_manager_remove_output_windows(
1374 media->priv->manager, media,
1375 stream->session->id, stream->participant);
1378 iter = purple_media_get_session_ids(media);
1379 for (; iter; iter = g_list_delete_link(iter, iter)) {
1380 gchar *session_name = iter->data;
1381 purple_media_manager_remove_output_windows(
1382 media->priv->manager, media,
1383 session_name, NULL);
1385 #endif
1388 #ifdef USE_GSTREAMER
1389 GstElement *
1390 purple_media_get_tee(PurpleMedia *media,
1391 const gchar *session_id, const gchar *participant)
1393 #ifdef USE_VV
1394 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1396 if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
1397 return purple_media_backend_fs2_get_tee(
1398 PURPLE_MEDIA_BACKEND_FS2(
1399 media->priv->backend),
1400 session_id, participant);
1401 g_return_val_if_reached(NULL);
1402 #else
1403 return NULL;
1404 #endif
1406 #endif /* USE_GSTREAMER */