Adapt migration for files
[pidgin-git.git] / libpurple / media.c
blob02bd4b9b8b714660cb1164f8cd13add4073ca9e9
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 "marshallers.h"
33 #include "media-gst.h"
34 #endif /* USE_GSTREAMER */
36 #ifdef USE_VV
37 /** @copydoc _PurpleMediaSession */
38 typedef struct _PurpleMediaSession PurpleMediaSession;
39 /** @copydoc _PurpleMediaStream */
40 typedef struct _PurpleMediaStream PurpleMediaStream;
42 struct _PurpleMediaSession
44 gchar *id;
45 PurpleMedia *media;
46 PurpleMediaSessionType type;
47 gboolean initiator;
50 struct _PurpleMediaStream
52 PurpleMediaSession *session;
53 gchar *participant;
55 GList *local_candidates;
56 GList *remote_candidates;
58 gboolean initiator;
59 gboolean accepted;
60 gboolean candidates_prepared;
62 GList *active_local_candidates;
63 GList *active_remote_candidates;
65 #endif
67 struct _PurpleMediaPrivate
69 #ifdef USE_VV
70 PurpleMediaManager *manager;
71 PurpleAccount *account;
72 PurpleMediaBackend *backend;
73 gchar *conference_type;
74 gboolean initiator;
75 gpointer protocol_data;
77 GHashTable *sessions; /* PurpleMediaSession table */
78 GList *participants;
79 GList *streams; /* PurpleMediaStream table */
80 #else
81 gpointer dummy;
82 #endif
85 #ifdef USE_VV
86 #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate))
88 static void purple_media_class_init (PurpleMediaClass *klass);
89 static void purple_media_init (PurpleMedia *media);
90 static void purple_media_dispose (GObject *object);
91 static void purple_media_finalize (GObject *object);
92 static void purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
93 static void purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
95 static void purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
96 const gchar *sess_id, const gchar *participant,
97 PurpleMediaCandidate *candidate, PurpleMedia *media);
98 static void purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
99 const gchar *sess_id, const gchar *name, PurpleMedia *media);
100 static void purple_media_candidate_pair_established_cb(
101 PurpleMediaBackend *backend,
102 const gchar *sess_id, const gchar *name,
103 PurpleMediaCandidate *local_candidate,
104 PurpleMediaCandidate *remote_candidate,
105 PurpleMedia *media);
106 static void purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
107 const gchar *sess_id, PurpleMedia *media);
109 static GObjectClass *parent_class = NULL;
113 enum {
114 S_ERROR,
115 CANDIDATES_PREPARED,
116 CODECS_CHANGED,
117 LEVEL,
118 NEW_CANDIDATE,
119 STATE_CHANGED,
120 STREAM_INFO,
121 CANDIDATE_PAIR_ESTABLISHED,
122 LAST_SIGNAL
124 static guint purple_media_signals[LAST_SIGNAL] = {0};
126 enum {
127 PROP_0,
128 PROP_MANAGER,
129 PROP_BACKEND,
130 PROP_ACCOUNT,
131 PROP_CONFERENCE_TYPE,
132 PROP_INITIATOR,
133 PROP_PROTOCOL_DATA,
135 #endif
138 GType
139 purple_media_get_type()
141 #ifdef USE_VV
142 static GType type = 0;
144 if (type == 0) {
145 static const GTypeInfo info = {
146 sizeof(PurpleMediaClass),
147 NULL,
148 NULL,
149 (GClassInitFunc) purple_media_class_init,
150 NULL,
151 NULL,
152 sizeof(PurpleMedia),
154 (GInstanceInitFunc) purple_media_init,
155 NULL
157 type = g_type_register_static(G_TYPE_OBJECT, "PurpleMedia", &info, 0);
159 return type;
160 #else
161 return G_TYPE_NONE;
162 #endif
165 #ifdef USE_VV
166 static void
167 purple_media_class_init (PurpleMediaClass *klass)
169 GObjectClass *gobject_class = (GObjectClass*)klass;
170 parent_class = g_type_class_peek_parent(klass);
172 gobject_class->dispose = purple_media_dispose;
173 gobject_class->finalize = purple_media_finalize;
174 gobject_class->set_property = purple_media_set_property;
175 gobject_class->get_property = purple_media_get_property;
177 g_object_class_install_property(gobject_class, PROP_MANAGER,
178 g_param_spec_object("manager",
179 "Purple Media Manager",
180 "The media manager that contains this media session.",
181 PURPLE_TYPE_MEDIA_MANAGER,
182 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
183 G_PARAM_STATIC_STRINGS));
186 * This one should be PURPLE_TYPE_MEDIA_BACKEND, but it doesn't
187 * like interfaces because they "aren't GObjects"
189 g_object_class_install_property(gobject_class, PROP_BACKEND,
190 g_param_spec_object("backend",
191 "Purple Media Backend",
192 "The backend object this media object uses.",
193 G_TYPE_OBJECT,
194 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
196 g_object_class_install_property(gobject_class, PROP_ACCOUNT,
197 g_param_spec_object("account", "PurpleAccount",
198 "The account this media session is on.",
199 PURPLE_TYPE_ACCOUNT,
200 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
201 G_PARAM_STATIC_STRINGS));
203 g_object_class_install_property(gobject_class, PROP_CONFERENCE_TYPE,
204 g_param_spec_string("conference-type",
205 "Conference Type",
206 "The type of conference that this media object "
207 "has been created to provide.",
208 NULL,
209 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
210 G_PARAM_STATIC_STRINGS));
212 g_object_class_install_property(gobject_class, PROP_INITIATOR,
213 g_param_spec_boolean("initiator",
214 "initiator",
215 "If the local user initiated the conference.",
216 FALSE,
217 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE |
218 G_PARAM_STATIC_STRINGS));
220 g_object_class_install_property(gobject_class, PROP_PROTOCOL_DATA,
221 g_param_spec_pointer("protocol-data",
222 "gpointer",
223 "Data the protocol set on the media session.",
224 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
226 purple_media_signals[S_ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(klass),
227 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
228 g_cclosure_marshal_VOID__STRING,
229 G_TYPE_NONE, 1, G_TYPE_STRING);
230 purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass),
231 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
232 purple_smarshal_VOID__STRING_STRING,
233 G_TYPE_NONE, 2, G_TYPE_STRING,
234 G_TYPE_STRING);
235 purple_media_signals[CODECS_CHANGED] = g_signal_new("codecs-changed", G_TYPE_FROM_CLASS(klass),
236 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
237 g_cclosure_marshal_VOID__STRING,
238 G_TYPE_NONE, 1, G_TYPE_STRING);
239 purple_media_signals[LEVEL] = g_signal_new("level", G_TYPE_FROM_CLASS(klass),
240 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
241 purple_smarshal_VOID__STRING_STRING_DOUBLE,
242 G_TYPE_NONE, 3, G_TYPE_STRING,
243 G_TYPE_STRING, G_TYPE_DOUBLE);
244 purple_media_signals[NEW_CANDIDATE] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass),
245 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
246 purple_smarshal_VOID__POINTER_POINTER_OBJECT,
247 G_TYPE_NONE, 3, G_TYPE_POINTER,
248 G_TYPE_POINTER, PURPLE_TYPE_MEDIA_CANDIDATE);
249 purple_media_signals[STATE_CHANGED] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass),
250 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
251 purple_smarshal_VOID__ENUM_STRING_STRING,
252 G_TYPE_NONE, 3, PURPLE_MEDIA_TYPE_STATE,
253 G_TYPE_STRING, G_TYPE_STRING);
254 purple_media_signals[STREAM_INFO] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass),
255 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
256 purple_smarshal_VOID__ENUM_STRING_STRING_BOOLEAN,
257 G_TYPE_NONE, 4, PURPLE_MEDIA_TYPE_INFO_TYPE,
258 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
259 purple_media_signals[CANDIDATE_PAIR_ESTABLISHED] = g_signal_new("candidate-pair-established", G_TYPE_FROM_CLASS(klass),
260 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
261 purple_smarshal_VOID__POINTER_POINTER_OBJECT_OBJECT,
262 G_TYPE_NONE, 4, G_TYPE_POINTER, G_TYPE_POINTER,
263 PURPLE_TYPE_MEDIA_CANDIDATE, PURPLE_TYPE_MEDIA_CANDIDATE);
264 g_type_class_add_private(klass, sizeof(PurpleMediaPrivate));
268 static void
269 purple_media_init (PurpleMedia *media)
271 media->priv = PURPLE_MEDIA_GET_PRIVATE(media);
272 memset(media->priv, 0, sizeof(*media->priv));
275 static void
276 purple_media_stream_free(PurpleMediaStream *stream)
278 if (stream == NULL)
279 return;
281 g_free(stream->participant);
283 if (stream->local_candidates)
284 purple_media_candidate_list_free(stream->local_candidates);
285 if (stream->remote_candidates)
286 purple_media_candidate_list_free(stream->remote_candidates);
288 if (stream->active_local_candidates)
289 purple_media_candidate_list_free(
290 stream->active_local_candidates);
291 if (stream->active_remote_candidates)
292 purple_media_candidate_list_free(
293 stream->active_remote_candidates);
295 g_free(stream);
298 static void
299 purple_media_session_free(PurpleMediaSession *session)
301 if (session == NULL)
302 return;
304 g_free(session->id);
305 g_free(session);
308 static void
309 purple_media_dispose(GObject *media)
311 PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
313 purple_debug_info("media","purple_media_dispose\n");
315 purple_media_manager_remove_media(priv->manager, PURPLE_MEDIA(media));
317 if (priv->backend) {
318 g_object_unref(priv->backend);
319 priv->backend = NULL;
322 if (priv->manager) {
323 g_object_unref(priv->manager);
324 priv->manager = NULL;
327 G_OBJECT_CLASS(parent_class)->dispose(media);
330 static void
331 purple_media_finalize(GObject *media)
333 PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
334 purple_debug_info("media","purple_media_finalize\n");
336 for (; priv->streams; priv->streams = g_list_delete_link(priv->streams, priv->streams))
337 purple_media_stream_free(priv->streams->data);
339 for (; priv->participants; priv->participants = g_list_delete_link(
340 priv->participants, priv->participants))
341 g_free(priv->participants->data);
343 if (priv->sessions) {
344 GList *sessions = g_hash_table_get_values(priv->sessions);
345 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
346 purple_media_session_free(sessions->data);
348 g_hash_table_destroy(priv->sessions);
351 G_OBJECT_CLASS(parent_class)->finalize(media);
354 static void
355 purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
357 PurpleMedia *media;
358 g_return_if_fail(PURPLE_IS_MEDIA(object));
360 media = PURPLE_MEDIA(object);
362 switch (prop_id) {
363 case PROP_MANAGER:
364 media->priv->manager = g_value_dup_object(value);
365 break;
366 case PROP_ACCOUNT:
367 media->priv->account = g_value_get_object(value);
368 break;
369 case PROP_CONFERENCE_TYPE:
370 media->priv->conference_type =
371 g_value_dup_string(value);
372 media->priv->backend = g_object_new(
373 purple_media_manager_get_backend_type(
374 purple_media_manager_get()),
375 "conference-type",
376 media->priv->conference_type,
377 "media", media,
378 NULL);
379 g_signal_connect(media->priv->backend,
380 "active-candidate-pair",
381 G_CALLBACK(
382 purple_media_candidate_pair_established_cb),
383 media);
384 g_signal_connect(media->priv->backend,
385 "candidates-prepared",
386 G_CALLBACK(
387 purple_media_candidates_prepared_cb),
388 media);
389 g_signal_connect(media->priv->backend,
390 "codecs-changed",
391 G_CALLBACK(
392 purple_media_codecs_changed_cb),
393 media);
394 g_signal_connect(media->priv->backend,
395 "new-candidate",
396 G_CALLBACK(
397 purple_media_new_local_candidate_cb),
398 media);
399 break;
400 case PROP_INITIATOR:
401 media->priv->initiator = g_value_get_boolean(value);
402 break;
403 case PROP_PROTOCOL_DATA:
404 media->priv->protocol_data = g_value_get_pointer(value);
405 break;
406 default:
407 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
408 break;
412 static void
413 purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
415 PurpleMedia *media;
416 g_return_if_fail(PURPLE_IS_MEDIA(object));
418 media = PURPLE_MEDIA(object);
420 switch (prop_id) {
421 case PROP_MANAGER:
422 g_value_set_object(value, media->priv->manager);
423 break;
424 case PROP_BACKEND:
425 g_value_set_object(value, media->priv->backend);
426 break;
427 case PROP_ACCOUNT:
428 g_value_set_object(value, media->priv->account);
429 break;
430 case PROP_CONFERENCE_TYPE:
431 g_value_set_string(value,
432 media->priv->conference_type);
433 break;
434 case PROP_INITIATOR:
435 g_value_set_boolean(value, media->priv->initiator);
436 break;
437 case PROP_PROTOCOL_DATA:
438 g_value_set_pointer(value, media->priv->protocol_data);
439 break;
440 default:
441 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
442 break;
447 static PurpleMediaSession*
448 purple_media_get_session(PurpleMedia *media, const gchar *sess_id)
450 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
451 return (PurpleMediaSession*) (media->priv->sessions) ?
452 g_hash_table_lookup(media->priv->sessions, sess_id) : NULL;
455 static PurpleMediaStream*
456 purple_media_get_stream(PurpleMedia *media, const gchar *session, const gchar *participant)
458 GList *streams;
460 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
462 streams = media->priv->streams;
464 for (; streams; streams = g_list_next(streams)) {
465 PurpleMediaStream *stream = streams->data;
466 if (!strcmp(stream->session->id, session) &&
467 !strcmp(stream->participant, participant))
468 return stream;
471 return NULL;
474 static GList *
475 purple_media_get_streams(PurpleMedia *media, const gchar *session,
476 const gchar *participant)
478 GList *streams;
479 GList *ret = NULL;
481 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
483 streams = media->priv->streams;
485 for (; streams; streams = g_list_next(streams)) {
486 PurpleMediaStream *stream = streams->data;
487 if ((session == NULL ||
488 !strcmp(stream->session->id, session)) &&
489 (participant == NULL ||
490 !strcmp(stream->participant, participant)))
491 ret = g_list_append(ret, stream);
494 return ret;
497 static void
498 purple_media_add_session(PurpleMedia *media, PurpleMediaSession *session)
500 g_return_if_fail(PURPLE_IS_MEDIA(media));
501 g_return_if_fail(session != NULL);
503 if (!media->priv->sessions) {
504 purple_debug_info("media", "Creating hash table for sessions\n");
505 media->priv->sessions = g_hash_table_new_full(g_str_hash, g_str_equal,
506 g_free, NULL);
508 g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session);
511 #if 0
512 static gboolean
513 purple_media_remove_session(PurpleMedia *media, PurpleMediaSession *session)
515 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
516 return g_hash_table_remove(media->priv->sessions, session->id);
518 #endif
520 static PurpleMediaStream *
521 purple_media_insert_stream(PurpleMediaSession *session,
522 const gchar *name, gboolean initiator)
524 PurpleMediaStream *media_stream;
526 g_return_val_if_fail(session != NULL, NULL);
528 media_stream = g_new0(PurpleMediaStream, 1);
529 media_stream->participant = g_strdup(name);
530 media_stream->session = session;
531 media_stream->initiator = initiator;
533 session->media->priv->streams =
534 g_list_append(session->media->priv->streams, media_stream);
536 return media_stream;
539 static void
540 purple_media_insert_local_candidate(PurpleMediaSession *session, const gchar *name,
541 PurpleMediaCandidate *candidate)
543 PurpleMediaStream *stream;
545 g_return_if_fail(session != NULL);
547 stream = purple_media_get_stream(session->media, session->id, name);
548 stream->local_candidates = g_list_append(stream->local_candidates, candidate);
550 #endif
552 GList *
553 purple_media_get_session_ids(PurpleMedia *media)
555 #ifdef USE_VV
556 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
557 return media->priv->sessions != NULL ?
558 g_hash_table_get_keys(media->priv->sessions) : NULL;
559 #else
560 return NULL;
561 #endif
564 #ifdef USE_VV
565 GstElement *
566 purple_media_get_src(PurpleMedia *media, const gchar *sess_id)
568 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
570 if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
571 return purple_media_backend_fs2_get_src(
572 PURPLE_MEDIA_BACKEND_FS2(
573 media->priv->backend), sess_id);
575 g_return_val_if_reached(NULL);
577 #endif /* USE_VV */
579 PurpleAccount *
580 purple_media_get_account(PurpleMedia *media)
582 #ifdef USE_VV
583 PurpleAccount *account;
584 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
585 g_object_get(G_OBJECT(media), "account", &account, NULL);
586 return account;
587 #else
588 return NULL;
589 #endif
592 gpointer
593 purple_media_get_protocol_data(PurpleMedia *media)
595 #ifdef USE_VV
596 gpointer protocol_data;
597 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
598 g_object_get(G_OBJECT(media), "protocol-data", &protocol_data, NULL);
599 return protocol_data;
600 #else
601 return NULL;
602 #endif
605 void
606 purple_media_set_protocol_data(PurpleMedia *media, gpointer protocol_data)
608 #ifdef USE_VV
609 g_return_if_fail(PURPLE_IS_MEDIA(media));
610 g_object_set(G_OBJECT(media), "protocol-data", protocol_data, NULL);
611 #endif
614 void
615 purple_media_error(PurpleMedia *media, const gchar *error, ...)
617 #ifdef USE_VV
618 va_list args;
619 gchar *message;
621 g_return_if_fail(PURPLE_IS_MEDIA(media));
623 va_start(args, error);
624 message = g_strdup_vprintf(error, args);
625 va_end(args);
627 purple_debug_error("media", "%s\n", message);
628 g_signal_emit(media, purple_media_signals[S_ERROR], 0, message);
630 g_free(message);
631 #endif
634 void
635 purple_media_end(PurpleMedia *media,
636 const gchar *session_id, const gchar *participant)
638 #ifdef USE_VV
639 GList *iter, *sessions = NULL, *participants = NULL;
641 g_return_if_fail(PURPLE_IS_MEDIA(media));
643 iter = purple_media_get_streams(media, session_id, participant);
645 /* Free matching streams */
646 for (; iter; iter = g_list_delete_link(iter, iter)) {
647 PurpleMediaStream *stream = iter->data;
649 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
650 0, PURPLE_MEDIA_STATE_END,
651 stream->session->id, stream->participant);
653 media->priv->streams =
654 g_list_remove(media->priv->streams, stream);
656 if (g_list_find(sessions, stream->session) == NULL)
657 sessions = g_list_prepend(sessions, stream->session);
659 if (g_list_find_custom(participants, stream->participant,
660 (GCompareFunc)strcmp) == NULL)
661 participants = g_list_prepend(participants,
662 g_strdup(stream->participant));
664 purple_media_stream_free(stream);
667 iter = media->priv->streams;
669 /* Reduce to list of sessions to remove */
670 for (; iter; iter = g_list_next(iter)) {
671 PurpleMediaStream *stream = iter->data;
673 sessions = g_list_remove(sessions, stream->session);
676 /* Free sessions with no streams left */
677 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
678 PurpleMediaSession *session = sessions->data;
680 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
681 0, PURPLE_MEDIA_STATE_END,
682 session->id, NULL);
684 g_hash_table_remove(media->priv->sessions, session->id);
685 purple_media_session_free(session);
688 iter = media->priv->streams;
690 /* Reduce to list of participants to remove */
691 for (; iter; iter = g_list_next(iter)) {
692 PurpleMediaStream *stream = iter->data;
693 GList *tmp;
695 tmp = g_list_find_custom(participants,
696 stream->participant, (GCompareFunc)strcmp);
698 if (tmp != NULL) {
699 g_free(tmp->data);
700 participants = g_list_delete_link(participants, tmp);
704 /* Remove participants with no streams left (just emit the signal) */
705 for (; participants; participants =
706 g_list_delete_link(participants, participants)) {
707 gchar *participant = participants->data;
708 GList *link = g_list_find_custom(media->priv->participants,
709 participant, (GCompareFunc)strcmp);
711 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
712 0, PURPLE_MEDIA_STATE_END,
713 NULL, participant);
715 if (link != NULL) {
716 g_free(link->data);
717 media->priv->participants = g_list_delete_link(
718 media->priv->participants, link);
721 g_free(participant);
724 /* Free the conference if no sessions left */
725 if (media->priv->sessions != NULL &&
726 g_hash_table_size(media->priv->sessions) == 0) {
727 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
728 0, PURPLE_MEDIA_STATE_END,
729 NULL, NULL);
730 g_object_unref(media);
731 return;
733 #endif
736 void
737 purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
738 const gchar *session_id, const gchar *participant,
739 gboolean local)
741 #ifdef USE_VV
742 g_return_if_fail(PURPLE_IS_MEDIA(media));
744 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
745 GList *streams, *sessions = NULL, *participants = NULL;
747 g_return_if_fail(PURPLE_IS_MEDIA(media));
749 streams = purple_media_get_streams(media,
750 session_id, participant);
752 /* Emit stream acceptance */
753 for (; streams; streams =
754 g_list_delete_link(streams, streams)) {
755 PurpleMediaStream *stream = streams->data;
757 stream->accepted = TRUE;
759 g_signal_emit(media,
760 purple_media_signals[STREAM_INFO],
761 0, type, stream->session->id,
762 stream->participant, local);
764 if (g_list_find(sessions, stream->session) == NULL)
765 sessions = g_list_prepend(sessions,
766 stream->session);
768 if (g_list_find_custom(participants,
769 stream->participant,
770 (GCompareFunc)strcmp) == NULL)
771 participants = g_list_prepend(participants,
772 g_strdup(stream->participant));
775 /* Emit session acceptance */
776 for (; sessions; sessions =
777 g_list_delete_link(sessions, sessions)) {
778 PurpleMediaSession *session = sessions->data;
780 if (purple_media_accepted(media, session->id, NULL))
781 g_signal_emit(media, purple_media_signals[
782 STREAM_INFO], 0,
783 PURPLE_MEDIA_INFO_ACCEPT,
784 session->id, NULL, local);
787 /* Emit participant acceptance */
788 for (; participants; participants = g_list_delete_link(
789 participants, participants)) {
790 gchar *participant = participants->data;
792 if (purple_media_accepted(media, NULL, participant))
793 g_signal_emit(media, purple_media_signals[
794 STREAM_INFO], 0,
795 PURPLE_MEDIA_INFO_ACCEPT,
796 NULL, participant, local);
798 g_free(participant);
801 /* Emit conference acceptance */
802 if (purple_media_accepted(media, NULL, NULL))
803 g_signal_emit(media,
804 purple_media_signals[STREAM_INFO],
805 0, PURPLE_MEDIA_INFO_ACCEPT,
806 NULL, NULL, local);
808 return;
809 } else if (type == PURPLE_MEDIA_INFO_HANGUP ||
810 type == PURPLE_MEDIA_INFO_REJECT) {
811 GList *streams;
813 g_return_if_fail(PURPLE_IS_MEDIA(media));
815 streams = purple_media_get_streams(media,
816 session_id, participant);
818 /* Emit for stream */
819 for (; streams; streams =
820 g_list_delete_link(streams, streams)) {
821 PurpleMediaStream *stream = streams->data;
823 g_signal_emit(media,
824 purple_media_signals[STREAM_INFO],
825 0, type, stream->session->id,
826 stream->participant, local);
829 if (session_id != NULL && participant != NULL) {
830 /* Everything that needs to be emitted has been */
831 } else if (session_id == NULL && participant == NULL) {
832 /* Emit for everything in the conference */
833 GList *sessions = NULL;
834 GList *participants = media->priv->participants;
836 if (media->priv->sessions != NULL)
837 sessions = g_hash_table_get_values(
838 media->priv->sessions);
840 /* Emit for sessions */
841 for (; sessions; sessions = g_list_delete_link(
842 sessions, sessions)) {
843 PurpleMediaSession *session = sessions->data;
845 g_signal_emit(media, purple_media_signals[
846 STREAM_INFO], 0, type,
847 session->id, NULL, local);
850 /* Emit for participants */
851 for (; participants; participants =
852 g_list_next(participants)) {
853 gchar *participant = participants->data;
855 g_signal_emit(media, purple_media_signals[
856 STREAM_INFO], 0, type,
857 NULL, participant, local);
860 /* Emit for conference */
861 g_signal_emit(media,
862 purple_media_signals[STREAM_INFO],
863 0, type, NULL, NULL, local);
864 } else if (session_id != NULL) {
865 /* Emit just the specific session */
866 PurpleMediaSession *session =
867 purple_media_get_session(
868 media, session_id);
870 if (session == NULL) {
871 purple_debug_warning("media",
872 "Couldn't find session"
873 " to hangup/reject.\n");
874 } else {
875 g_signal_emit(media, purple_media_signals[
876 STREAM_INFO], 0, type,
877 session->id, NULL, local);
879 } else if (participant != NULL) {
880 /* Emit just the specific participant */
881 if (!g_list_find_custom(media->priv->participants,
882 participant, (GCompareFunc)strcmp)) {
883 purple_debug_warning("media",
884 "Couldn't find participant"
885 " to hangup/reject.\n");
886 } else {
887 g_signal_emit(media, purple_media_signals[
888 STREAM_INFO], 0, type, NULL,
889 participant, local);
893 purple_media_end(media, session_id, participant);
894 return;
897 g_signal_emit(media, purple_media_signals[STREAM_INFO],
898 0, type, session_id, participant, local);
899 #endif
902 void
903 purple_media_set_params(PurpleMedia *media,
904 guint num_params, GParameter *params)
906 #ifdef USE_VV
907 g_return_if_fail(PURPLE_IS_MEDIA(media));
909 purple_media_backend_set_params(media->priv->backend, num_params, params);
910 #endif
913 const gchar **
914 purple_media_get_available_params(PurpleMedia *media)
916 static const gchar *NULL_ARRAY[] = { NULL };
917 #ifdef USE_VV
918 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL_ARRAY);
920 return purple_media_backend_get_available_params(media->priv->backend);
921 #else
922 return NULL_ARRAY;
923 #endif
926 gboolean
927 purple_media_param_is_supported(PurpleMedia *media, const gchar *param)
929 #ifdef USE_VV
930 const gchar **params;
932 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
933 g_return_val_if_fail(param != NULL, FALSE);
935 params = purple_media_backend_get_available_params(media->priv->backend);
936 for (; *params != NULL; ++params)
937 if (!strcmp(*params, param))
938 return TRUE;
939 #endif
940 return FALSE;
943 #ifdef USE_VV
944 static void
945 purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
946 const gchar *sess_id, const gchar *participant,
947 PurpleMediaCandidate *candidate, PurpleMedia *media)
949 PurpleMediaSession *session =
950 purple_media_get_session(media, sess_id);
952 purple_media_insert_local_candidate(session, participant,
953 purple_media_candidate_copy(candidate));
955 g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE],
956 0, session->id, participant, candidate);
959 static void
960 purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
961 const gchar *sess_id, const gchar *name, PurpleMedia *media)
963 PurpleMediaStream *stream_data;
965 g_return_if_fail(PURPLE_IS_MEDIA(media));
967 stream_data = purple_media_get_stream(media, sess_id, name);
968 stream_data->candidates_prepared = TRUE;
970 g_signal_emit(media, purple_media_signals[CANDIDATES_PREPARED],
971 0, sess_id, name);
974 /* callback called when a pair of transport candidates (local and remote)
975 * has been established */
976 static void
977 purple_media_candidate_pair_established_cb(PurpleMediaBackend *backend,
978 const gchar *sess_id, const gchar *name,
979 PurpleMediaCandidate *local_candidate,
980 PurpleMediaCandidate *remote_candidate,
981 PurpleMedia *media)
983 PurpleMediaStream *stream;
984 GList *iter;
985 guint id;
987 g_return_if_fail(PURPLE_IS_MEDIA(media));
989 stream = purple_media_get_stream(media, sess_id, name);
990 id = purple_media_candidate_get_component_id(local_candidate);
992 iter = stream->active_local_candidates;
993 for(; iter; iter = g_list_next(iter)) {
994 PurpleMediaCandidate *c = iter->data;
995 if (id == purple_media_candidate_get_component_id(c)) {
996 g_object_unref(c);
997 stream->active_local_candidates =
998 g_list_delete_link(iter, iter);
999 stream->active_local_candidates = g_list_prepend(
1000 stream->active_local_candidates,
1001 purple_media_candidate_copy(
1002 local_candidate));
1003 break;
1006 if (iter == NULL)
1007 stream->active_local_candidates = g_list_prepend(
1008 stream->active_local_candidates,
1009 purple_media_candidate_copy(
1010 local_candidate));
1012 id = purple_media_candidate_get_component_id(local_candidate);
1014 iter = stream->active_remote_candidates;
1015 for(; iter; iter = g_list_next(iter)) {
1016 PurpleMediaCandidate *c = iter->data;
1017 if (id == purple_media_candidate_get_component_id(c)) {
1018 g_object_unref(c);
1019 stream->active_remote_candidates =
1020 g_list_delete_link(iter, iter);
1021 stream->active_remote_candidates = g_list_prepend(
1022 stream->active_remote_candidates,
1023 purple_media_candidate_copy(
1024 remote_candidate));
1025 break;
1028 if (iter == NULL)
1029 stream->active_remote_candidates = g_list_prepend(
1030 stream->active_remote_candidates,
1031 purple_media_candidate_copy(
1032 remote_candidate));
1034 g_signal_emit(media, purple_media_signals[CANDIDATE_PAIR_ESTABLISHED],
1035 0, sess_id, name, local_candidate, remote_candidate);
1036 purple_debug_info("media", "candidate pair established\n");
1039 static void
1040 purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
1041 const gchar *sess_id, PurpleMedia *media)
1043 g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, sess_id);
1045 #endif /* USE_VV */
1047 gboolean
1048 purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
1049 const gchar *who, PurpleMediaSessionType type,
1050 gboolean initiator, const gchar *transmitter,
1051 guint num_params, GParameter *params)
1053 #ifdef USE_VV
1054 PurpleMediaSession *session;
1056 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1058 if (!purple_media_backend_add_stream(media->priv->backend,
1059 sess_id, who, type, initiator, transmitter,
1060 num_params, params)) {
1061 purple_debug_error("media", "Error adding stream.\n");
1062 return FALSE;
1065 session = purple_media_get_session(media, sess_id);
1067 if (!session) {
1068 session = g_new0(PurpleMediaSession, 1);
1069 session->id = g_strdup(sess_id);
1070 session->media = media;
1071 session->type = type;
1072 session->initiator = initiator;
1074 purple_media_add_session(media, session);
1075 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
1076 0, PURPLE_MEDIA_STATE_NEW,
1077 session->id, NULL);
1080 if (!g_list_find_custom(media->priv->participants,
1081 who, (GCompareFunc)strcmp)) {
1082 media->priv->participants = g_list_prepend(
1083 media->priv->participants, g_strdup(who));
1085 g_signal_emit(media, purple_media_signals[STATE_CHANGED], 0,
1086 PURPLE_MEDIA_STATE_NEW, NULL, who);
1089 if (purple_media_get_stream(media, sess_id, who) == NULL) {
1090 purple_media_insert_stream(session, who, initiator);
1092 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
1093 0, PURPLE_MEDIA_STATE_NEW,
1094 session->id, who);
1097 return TRUE;
1098 #else
1099 return FALSE;
1100 #endif /* USE_VV */
1103 PurpleMediaManager *
1104 purple_media_get_manager(PurpleMedia *media)
1106 PurpleMediaManager *ret;
1107 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1108 g_object_get(media, "manager", &ret, NULL);
1109 return ret;
1112 PurpleMediaSessionType
1113 purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id)
1115 #ifdef USE_VV
1116 PurpleMediaSession *session;
1117 g_return_val_if_fail(PURPLE_IS_MEDIA(media), PURPLE_MEDIA_NONE);
1118 session = purple_media_get_session(media, sess_id);
1119 return session->type;
1120 #else
1121 return PURPLE_MEDIA_NONE;
1122 #endif
1124 /* XXX: Should wait until codecs-ready is TRUE before using this function */
1125 GList *
1126 purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id)
1128 #ifdef USE_VV
1129 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1131 return purple_media_backend_get_codecs(media->priv->backend, sess_id);
1132 #else
1133 return NULL;
1134 #endif
1137 GList *
1138 purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id,
1139 const gchar *participant)
1141 #ifdef USE_VV
1142 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1144 return purple_media_backend_get_local_candidates(media->priv->backend,
1145 sess_id, participant);
1146 #else
1147 return NULL;
1148 #endif
1151 void
1152 purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id,
1153 const gchar *participant,
1154 GList *remote_candidates)
1156 #ifdef USE_VV
1157 PurpleMediaStream *stream;
1159 g_return_if_fail(PURPLE_IS_MEDIA(media));
1160 stream = purple_media_get_stream(media, sess_id, participant);
1162 if (stream == NULL) {
1163 purple_debug_error("media",
1164 "purple_media_add_remote_candidates: "
1165 "couldn't find stream %s %s.\n",
1166 sess_id ? sess_id : "(null)",
1167 participant ? participant : "(null)");
1168 return;
1171 stream->remote_candidates = g_list_concat(stream->remote_candidates,
1172 purple_media_candidate_list_copy(remote_candidates));
1174 purple_media_backend_add_remote_candidates(media->priv->backend,
1175 sess_id, participant, remote_candidates);
1176 #endif
1179 GList *
1180 purple_media_get_active_local_candidates(PurpleMedia *media,
1181 const gchar *sess_id, const gchar *participant)
1183 #ifdef USE_VV
1184 PurpleMediaStream *stream;
1185 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1186 stream = purple_media_get_stream(media, sess_id, participant);
1187 return purple_media_candidate_list_copy(
1188 stream->active_local_candidates);
1189 #else
1190 return NULL;
1191 #endif
1194 GList *
1195 purple_media_get_active_remote_candidates(PurpleMedia *media,
1196 const gchar *sess_id, const gchar *participant)
1198 #ifdef USE_VV
1199 PurpleMediaStream *stream;
1200 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1201 stream = purple_media_get_stream(media, sess_id, participant);
1202 return purple_media_candidate_list_copy(
1203 stream->active_remote_candidates);
1204 #else
1205 return NULL;
1206 #endif
1209 gboolean
1210 purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
1211 const gchar *participant, GList *codecs)
1213 #ifdef USE_VV
1214 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1216 return purple_media_backend_set_remote_codecs(media->priv->backend,
1217 sess_id, participant, codecs);
1218 #else
1219 return FALSE;
1220 #endif
1223 gboolean
1224 purple_media_candidates_prepared(PurpleMedia *media,
1225 const gchar *session_id, const gchar *participant)
1227 #ifdef USE_VV
1228 GList *streams;
1229 gboolean prepared = TRUE;
1231 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1233 streams = purple_media_get_streams(media, session_id, participant);
1235 for (; streams; streams = g_list_delete_link(streams, streams)) {
1236 PurpleMediaStream *stream = streams->data;
1237 if (stream->candidates_prepared == FALSE) {
1238 g_list_free(streams);
1239 prepared = FALSE;
1240 break;
1244 return prepared;
1245 #else
1246 return FALSE;
1247 #endif
1250 gboolean
1251 purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec)
1253 #ifdef USE_VV
1254 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1256 return purple_media_backend_set_send_codec(
1257 media->priv->backend, sess_id, codec);
1258 #else
1259 return FALSE;
1260 #endif
1263 gboolean
1264 purple_media_set_encryption_parameters(PurpleMedia *media, const gchar *sess_id,
1265 const gchar *cipher, const gchar *auth,
1266 const gchar *key, gsize key_len)
1268 #ifdef USE_VV
1269 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1270 return purple_media_backend_set_encryption_parameters(media->priv->backend,
1271 sess_id, cipher, auth, key, key_len);
1272 #else
1273 return FALSE;
1274 #endif
1277 gboolean
1278 purple_media_set_decryption_parameters(PurpleMedia *media, const gchar *sess_id,
1279 const gchar *participant, const gchar *cipher,
1280 const gchar *auth, const gchar *key, gsize key_len)
1282 #ifdef USE_VV
1283 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1284 return purple_media_backend_set_decryption_parameters(media->priv->backend,
1285 sess_id, participant, cipher, auth, key, key_len);
1286 #else
1287 return FALSE;
1288 #endif
1291 gboolean
1292 purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id)
1294 #ifdef USE_VV
1295 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1297 return purple_media_backend_codecs_ready(
1298 media->priv->backend, sess_id);
1299 #else
1300 return FALSE;
1301 #endif
1304 gboolean
1305 purple_media_set_send_rtcp_mux(PurpleMedia *media, const gchar *sess_id,
1306 const gchar *participant, gboolean send_rtcp_mux)
1308 #ifdef USE_VV
1309 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1311 return purple_media_backend_set_send_rtcp_mux(media->priv->backend,
1312 sess_id, participant, send_rtcp_mux);
1313 #else
1314 return FALSE;
1315 #endif
1318 gboolean
1319 purple_media_is_initiator(PurpleMedia *media,
1320 const gchar *sess_id, const gchar *participant)
1322 #ifdef USE_VV
1323 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1325 if (sess_id == NULL && participant == NULL)
1326 return media->priv->initiator;
1327 else if (sess_id != NULL && participant == NULL) {
1328 PurpleMediaSession *session =
1329 purple_media_get_session(media, sess_id);
1330 return session != NULL ? session->initiator : FALSE;
1331 } else if (sess_id != NULL && participant != NULL) {
1332 PurpleMediaStream *stream = purple_media_get_stream(
1333 media, sess_id, participant);
1334 return stream != NULL ? stream->initiator : FALSE;
1336 #endif
1337 return FALSE;
1340 gboolean
1341 purple_media_accepted(PurpleMedia *media, const gchar *sess_id,
1342 const gchar *participant)
1344 #ifdef USE_VV
1345 gboolean accepted = TRUE;
1347 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1349 if (sess_id == NULL && participant == NULL) {
1350 GList *streams = media->priv->streams;
1352 for (; streams; streams = g_list_next(streams)) {
1353 PurpleMediaStream *stream = streams->data;
1354 if (stream->accepted == FALSE) {
1355 accepted = FALSE;
1356 break;
1359 } else if (sess_id != NULL && participant == NULL) {
1360 GList *streams = purple_media_get_streams(
1361 media, sess_id, NULL);
1362 for (; streams; streams =
1363 g_list_delete_link(streams, streams)) {
1364 PurpleMediaStream *stream = streams->data;
1365 if (stream->accepted == FALSE) {
1366 g_list_free(streams);
1367 accepted = FALSE;
1368 break;
1371 } else if (sess_id != NULL && participant != NULL) {
1372 PurpleMediaStream *stream = purple_media_get_stream(
1373 media, sess_id, participant);
1374 if (stream == NULL || stream->accepted == FALSE)
1375 accepted = FALSE;
1378 return accepted;
1379 #else
1380 return FALSE;
1381 #endif
1384 void purple_media_set_input_volume(PurpleMedia *media,
1385 const gchar *session_id, double level)
1387 #ifdef USE_VV
1388 g_return_if_fail(PURPLE_IS_MEDIA(media));
1389 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
1391 purple_media_backend_fs2_set_input_volume(
1392 PURPLE_MEDIA_BACKEND_FS2(
1393 media->priv->backend),
1394 session_id, level);
1395 #endif
1398 void purple_media_set_output_volume(PurpleMedia *media,
1399 const gchar *session_id, const gchar *participant,
1400 double level)
1402 #ifdef USE_VV
1403 g_return_if_fail(PURPLE_IS_MEDIA(media));
1404 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
1406 purple_media_backend_fs2_set_output_volume(
1407 PURPLE_MEDIA_BACKEND_FS2(
1408 media->priv->backend),
1409 session_id, participant, level);
1410 #endif
1413 gulong
1414 purple_media_set_output_window(PurpleMedia *media, const gchar *session_id,
1415 const gchar *participant, gulong window_id)
1417 #ifdef USE_VV
1418 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1420 return purple_media_manager_set_output_window(media->priv->manager,
1421 media, session_id, participant, window_id);
1422 #else
1423 return 0;
1424 #endif
1427 void
1428 purple_media_remove_output_windows(PurpleMedia *media)
1430 #ifdef USE_VV
1431 GList *iter = media->priv->streams;
1432 for (; iter; iter = g_list_next(iter)) {
1433 PurpleMediaStream *stream = iter->data;
1434 purple_media_manager_remove_output_windows(
1435 media->priv->manager, media,
1436 stream->session->id, stream->participant);
1439 iter = purple_media_get_session_ids(media);
1440 for (; iter; iter = g_list_delete_link(iter, iter)) {
1441 gchar *session_name = iter->data;
1442 purple_media_manager_remove_output_windows(
1443 media->priv->manager, media,
1444 session_name, NULL);
1446 #endif
1449 #ifdef USE_VV
1450 GstElement *
1451 purple_media_get_tee(PurpleMedia *media,
1452 const gchar *session_id, const gchar *participant)
1454 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1456 if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
1457 return purple_media_backend_fs2_get_tee(
1458 PURPLE_MEDIA_BACKEND_FS2(
1459 media->priv->backend),
1460 session_id, participant);
1461 g_return_val_if_reached(NULL);
1463 #endif /* USE_VV */
1465 gboolean
1466 purple_media_send_dtmf(PurpleMedia *media, const gchar *session_id,
1467 gchar dtmf, guint8 volume, guint16 duration)
1469 #ifdef USE_VV
1470 PurpleAccount *account = NULL;
1471 PurpleConnection *gc = NULL;
1472 PurpleProtocol *protocol = NULL;
1473 PurpleMediaBackendIface *backend_iface = NULL;
1475 if (media)
1477 account = purple_media_get_account(media);
1478 backend_iface = PURPLE_MEDIA_BACKEND_GET_INTERFACE(media->priv->backend);
1480 if (account)
1481 gc = purple_account_get_connection(account);
1482 if (gc)
1483 protocol = purple_connection_get_protocol(gc);
1485 if (dtmf == 'a')
1486 dtmf = 'A';
1487 else if (dtmf == 'b')
1488 dtmf = 'B';
1489 else if (dtmf == 'c')
1490 dtmf = 'C';
1491 else if (dtmf == 'd')
1492 dtmf = 'D';
1494 g_return_val_if_fail(strchr("0123456789ABCD#*", dtmf), FALSE);
1496 if (PURPLE_PROTOCOL_IMPLEMENTS(protocol, MEDIA_IFACE, send_dtmf)
1497 && purple_protocol_media_iface_send_dtmf(protocol, media, dtmf, volume, duration))
1499 return TRUE;
1500 } else if (backend_iface && backend_iface->send_dtmf
1501 && backend_iface->send_dtmf(media->priv->backend,
1502 session_id, dtmf, volume, duration))
1504 return TRUE;
1506 #endif
1507 return FALSE;