2
* @file media.c Media API
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
30
#include "media/backend-iface.h"
31
#include "mediamanager.h"
36
#include "media/backend-fs2.h"
37
#include "marshallers.h"
38
#include "media-gst.h"
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 */
61
GObject parent; /**< The parent of this object. */
62
PurpleMediaPrivate *priv; /**< The private data of this object. */
65
struct _PurpleMediaSession
69
PurpleMediaSessionType type;
73
struct _PurpleMediaStream
75
PurpleMediaSession *session;
78
GList *local_candidates;
79
GList *remote_candidates;
83
gboolean candidates_prepared;
85
GList *active_local_candidates;
86
GList *active_remote_candidates;
90
struct _PurpleMediaPrivate
93
PurpleMediaManager *manager;
94
PurpleAccount *account;
95
PurpleMediaBackend *backend;
96
gchar *conference_type;
100
GHashTable *sessions; /* PurpleMediaSession table */
102
GList *streams; /* PurpleMediaStream table */
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,
129
static void purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
130
const gchar *sess_id, PurpleMedia *media);
132
static GObjectClass *parent_class = NULL;
146
static guint purple_media_signals[LAST_SIGNAL] = {0};
153
PROP_CONFERENCE_TYPE,
161
purple_media_get_type()
164
static GType type = 0;
167
static const GTypeInfo info = {
168
sizeof(PurpleMediaClass),
171
(GClassInitFunc) purple_media_class_init,
176
(GInstanceInitFunc) purple_media_init,
179
type = g_type_register_static(G_TYPE_OBJECT, "PurpleMedia", &info, 0);
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.",
217
g_object_class_install_property(gobject_class, PROP_ACCOUNT,
218
g_param_spec_pointer("account",
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",
226
"The type of conference that this media object "
227
"has been created to provide.",
229
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
231
g_object_class_install_property(gobject_class, PROP_INITIATOR,
232
g_param_spec_boolean("initiator",
234
"If the local user initiated the conference.",
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",
241
"Data the prpl plugin set on the media session.",
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,
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));
282
purple_media_init (PurpleMedia *media)
284
media->priv = PURPLE_MEDIA_GET_PRIVATE(media);
285
memset(media->priv, 0, sizeof(*media->priv));
289
purple_media_stream_free(PurpleMediaStream *stream)
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);
312
purple_media_session_free(PurpleMediaSession *session)
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));
331
g_object_unref(priv->backend);
332
priv->backend = NULL;
336
g_object_unref(priv->manager);
337
priv->manager = NULL;
340
G_OBJECT_CLASS(parent_class)->dispose(media);
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);
368
purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
371
g_return_if_fail(PURPLE_IS_MEDIA(object));
373
media = PURPLE_MEDIA(object);
377
media->priv->manager = g_value_dup_object(value);
380
media->priv->account = g_value_get_pointer(value);
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()),
389
media->priv->conference_type,
392
g_signal_connect(media->priv->backend,
393
"active-candidate-pair",
395
purple_media_candidate_pair_established_cb),
397
g_signal_connect(media->priv->backend,
398
"candidates-prepared",
400
purple_media_candidates_prepared_cb),
402
g_signal_connect(media->priv->backend,
405
purple_media_codecs_changed_cb),
407
g_signal_connect(media->priv->backend,
410
purple_media_new_local_candidate_cb),
414
media->priv->initiator = g_value_get_boolean(value);
417
media->priv->prpl_data = g_value_get_pointer(value);
420
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
426
purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
429
g_return_if_fail(PURPLE_IS_MEDIA(object));
431
media = PURPLE_MEDIA(object);
435
g_value_set_object(value, media->priv->manager);
438
g_value_set_object(value, media->priv->backend);
441
g_value_set_pointer(value, media->priv->account);
443
case PROP_CONFERENCE_TYPE:
444
g_value_set_string(value,
445
media->priv->conference_type);
448
g_value_set_boolean(value, media->priv->initiator);
451
g_value_set_pointer(value, media->priv->prpl_data);
454
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
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)
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))
488
purple_media_get_streams(PurpleMedia *media, const gchar *session,
489
const gchar *participant)
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);
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_full(g_str_hash, g_str_equal,
521
g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session);
526
purple_media_remove_session(PurpleMedia *media, PurpleMediaSession *session)
528
g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
529
return g_hash_table_remove(media->priv->sessions, session->id);
533
static PurpleMediaStream *
534
purple_media_insert_stream(PurpleMediaSession *session,
535
const gchar *name, gboolean initiator)
537
PurpleMediaStream *media_stream;
539
g_return_val_if_fail(session != NULL, NULL);
541
media_stream = g_new0(PurpleMediaStream, 1);
542
media_stream->participant = g_strdup(name);
543
media_stream->session = session;
544
media_stream->initiator = initiator;
546
session->media->priv->streams =
547
g_list_append(session->media->priv->streams, media_stream);
553
purple_media_insert_local_candidate(PurpleMediaSession *session, const gchar *name,
554
PurpleMediaCandidate *candidate)
556
PurpleMediaStream *stream;
558
g_return_if_fail(session != NULL);
560
stream = purple_media_get_stream(session->media, session->id, name);
561
stream->local_candidates = g_list_append(stream->local_candidates, candidate);
566
purple_media_get_session_ids(PurpleMedia *media)
569
g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
570
return media->priv->sessions != NULL ?
571
g_hash_table_get_keys(media->priv->sessions) : NULL;
579
purple_media_get_src(PurpleMedia *media, const gchar *sess_id)
582
g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
584
if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
585
return purple_media_backend_fs2_get_src(
586
PURPLE_MEDIA_BACKEND_FS2(
587
media->priv->backend), sess_id);
589
g_return_val_if_reached(NULL);
594
#endif /* USE_GSTREAMER */
597
purple_media_get_account(PurpleMedia *media)
600
PurpleAccount *account;
601
g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
602
g_object_get(G_OBJECT(media), "account", &account, NULL);
610
purple_media_get_prpl_data(PurpleMedia *media)
614
g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
615
g_object_get(G_OBJECT(media), "prpl-data", &prpl_data, NULL);
623
purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data)
626
g_return_if_fail(PURPLE_IS_MEDIA(media));
627
g_object_set(G_OBJECT(media), "prpl-data", prpl_data, NULL);
632
purple_media_error(PurpleMedia *media, const gchar *error, ...)
638
g_return_if_fail(PURPLE_IS_MEDIA(media));
640
va_start(args, error);
641
message = g_strdup_vprintf(error, args);
644
purple_debug_error("media", "%s\n", message);
645
g_signal_emit(media, purple_media_signals[S_ERROR], 0, message);
652
purple_media_end(PurpleMedia *media,
653
const gchar *session_id, const gchar *participant)
656
GList *iter, *sessions = NULL, *participants = NULL;
658
g_return_if_fail(PURPLE_IS_MEDIA(media));
660
iter = purple_media_get_streams(media, session_id, participant);
662
/* Free matching streams */
663
for (; iter; iter = g_list_delete_link(iter, iter)) {
664
PurpleMediaStream *stream = iter->data;
666
g_signal_emit(media, purple_media_signals[STATE_CHANGED],
667
0, PURPLE_MEDIA_STATE_END,
668
stream->session->id, stream->participant);
670
media->priv->streams =
671
g_list_remove(media->priv->streams, stream);
673
if (g_list_find(sessions, stream->session) == NULL)
674
sessions = g_list_prepend(sessions, stream->session);
676
if (g_list_find_custom(participants, stream->participant,
677
(GCompareFunc)strcmp) == NULL)
678
participants = g_list_prepend(participants,
679
g_strdup(stream->participant));
681
purple_media_stream_free(stream);
684
iter = media->priv->streams;
686
/* Reduce to list of sessions to remove */
687
for (; iter; iter = g_list_next(iter)) {
688
PurpleMediaStream *stream = iter->data;
690
sessions = g_list_remove(sessions, stream->session);
693
/* Free sessions with no streams left */
694
for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
695
PurpleMediaSession *session = sessions->data;
697
g_signal_emit(media, purple_media_signals[STATE_CHANGED],
698
0, PURPLE_MEDIA_STATE_END,
701
g_hash_table_remove(media->priv->sessions, session->id);
702
purple_media_session_free(session);
705
iter = media->priv->streams;
707
/* Reduce to list of participants to remove */
708
for (; iter; iter = g_list_next(iter)) {
709
PurpleMediaStream *stream = iter->data;
712
tmp = g_list_find_custom(participants,
713
stream->participant, (GCompareFunc)strcmp);
717
participants = g_list_delete_link(participants, tmp);
721
/* Remove participants with no streams left (just emit the signal) */
722
for (; participants; participants =
723
g_list_delete_link(participants, participants)) {
724
gchar *participant = participants->data;
725
GList *link = g_list_find_custom(media->priv->participants,
726
participant, (GCompareFunc)strcmp);
728
g_signal_emit(media, purple_media_signals[STATE_CHANGED],
729
0, PURPLE_MEDIA_STATE_END,
734
media->priv->participants = g_list_delete_link(
735
media->priv->participants, link);
741
/* Free the conference if no sessions left */
742
if (media->priv->sessions != NULL &&
743
g_hash_table_size(media->priv->sessions) == 0) {
744
g_signal_emit(media, purple_media_signals[STATE_CHANGED],
745
0, PURPLE_MEDIA_STATE_END,
747
g_object_unref(media);
754
purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
755
const gchar *session_id, const gchar *participant,
759
g_return_if_fail(PURPLE_IS_MEDIA(media));
761
if (type == PURPLE_MEDIA_INFO_ACCEPT) {
762
GList *streams, *sessions = NULL, *participants = NULL;
764
g_return_if_fail(PURPLE_IS_MEDIA(media));
766
streams = purple_media_get_streams(media,
767
session_id, participant);
769
/* Emit stream acceptance */
770
for (; streams; streams =
771
g_list_delete_link(streams, streams)) {
772
PurpleMediaStream *stream = streams->data;
774
stream->accepted = TRUE;
777
purple_media_signals[STREAM_INFO],
778
0, type, stream->session->id,
779
stream->participant, local);
781
if (g_list_find(sessions, stream->session) == NULL)
782
sessions = g_list_prepend(sessions,
785
if (g_list_find_custom(participants,
787
(GCompareFunc)strcmp) == NULL)
788
participants = g_list_prepend(participants,
789
g_strdup(stream->participant));
792
/* Emit session acceptance */
793
for (; sessions; sessions =
794
g_list_delete_link(sessions, sessions)) {
795
PurpleMediaSession *session = sessions->data;
797
if (purple_media_accepted(media, session->id, NULL))
798
g_signal_emit(media, purple_media_signals[
800
PURPLE_MEDIA_INFO_ACCEPT,
801
session->id, NULL, local);
804
/* Emit participant acceptance */
805
for (; participants; participants = g_list_delete_link(
806
participants, participants)) {
807
gchar *participant = participants->data;
809
if (purple_media_accepted(media, NULL, participant))
810
g_signal_emit(media, purple_media_signals[
812
PURPLE_MEDIA_INFO_ACCEPT,
813
NULL, participant, local);
818
/* Emit conference acceptance */
819
if (purple_media_accepted(media, NULL, NULL))
821
purple_media_signals[STREAM_INFO],
822
0, PURPLE_MEDIA_INFO_ACCEPT,
826
} else if (type == PURPLE_MEDIA_INFO_HANGUP ||
827
type == PURPLE_MEDIA_INFO_REJECT) {
830
g_return_if_fail(PURPLE_IS_MEDIA(media));
832
streams = purple_media_get_streams(media,
833
session_id, participant);
835
/* Emit for stream */
836
for (; streams; streams =
837
g_list_delete_link(streams, streams)) {
838
PurpleMediaStream *stream = streams->data;
841
purple_media_signals[STREAM_INFO],
842
0, type, stream->session->id,
843
stream->participant, local);
846
if (session_id != NULL && participant != NULL) {
847
/* Everything that needs to be emitted has been */
848
} else if (session_id == NULL && participant == NULL) {
849
/* Emit for everything in the conference */
850
GList *sessions = NULL;
851
GList *participants = media->priv->participants;
853
if (media->priv->sessions != NULL)
854
sessions = g_hash_table_get_values(
855
media->priv->sessions);
857
/* Emit for sessions */
858
for (; sessions; sessions = g_list_delete_link(
859
sessions, sessions)) {
860
PurpleMediaSession *session = sessions->data;
862
g_signal_emit(media, purple_media_signals[
863
STREAM_INFO], 0, type,
864
session->id, NULL, local);
867
/* Emit for participants */
868
for (; participants; participants =
869
g_list_next(participants)) {
870
gchar *participant = participants->data;
872
g_signal_emit(media, purple_media_signals[
873
STREAM_INFO], 0, type,
874
NULL, participant, local);
877
/* Emit for conference */
879
purple_media_signals[STREAM_INFO],
880
0, type, NULL, NULL, local);
881
} else if (session_id != NULL) {
882
/* Emit just the specific session */
883
PurpleMediaSession *session =
884
purple_media_get_session(
887
if (session == NULL) {
888
purple_debug_warning("media",
889
"Couldn't find session"
890
" to hangup/reject.\n");
892
g_signal_emit(media, purple_media_signals[
893
STREAM_INFO], 0, type,
894
session->id, NULL, local);
896
} else if (participant != NULL) {
897
/* Emit just the specific participant */
898
if (!g_list_find_custom(media->priv->participants,
899
participant, (GCompareFunc)strcmp)) {
900
purple_debug_warning("media",
901
"Couldn't find participant"
902
" to hangup/reject.\n");
904
g_signal_emit(media, purple_media_signals[
905
STREAM_INFO], 0, type, NULL,
910
purple_media_end(media, session_id, participant);
914
g_signal_emit(media, purple_media_signals[STREAM_INFO],
915
0, type, session_id, participant, local);
920
purple_media_set_params(PurpleMedia *media,
921
guint num_params, GParameter *params)
924
g_return_if_fail(PURPLE_IS_MEDIA(media));
926
purple_media_backend_set_params(media->priv->backend, num_params, params);
931
purple_media_get_available_params(PurpleMedia *media)
933
static const gchar *NULL_ARRAY[] = { NULL };
935
g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL_ARRAY);
937
return purple_media_backend_get_available_params(media->priv->backend);
944
purple_media_param_is_supported(PurpleMedia *media, const gchar *param)
947
const gchar **params;
949
g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
950
g_return_val_if_fail(param != NULL, FALSE);
952
params = purple_media_backend_get_available_params(media->priv->backend);
953
for (; *params != NULL; ++params)
954
if (!strcmp(*params, param))
962
purple_media_new_local_candidate_cb(PurpleMediaBackend *backend,
963
const gchar *sess_id, const gchar *participant,
964
PurpleMediaCandidate *candidate, PurpleMedia *media)
966
PurpleMediaSession *session =
967
purple_media_get_session(media, sess_id);
969
purple_media_insert_local_candidate(session, participant,
970
purple_media_candidate_copy(candidate));
972
g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE],
973
0, session->id, participant, candidate);
977
purple_media_candidates_prepared_cb(PurpleMediaBackend *backend,
978
const gchar *sess_id, const gchar *name, PurpleMedia *media)
980
PurpleMediaStream *stream_data;
982
g_return_if_fail(PURPLE_IS_MEDIA(media));
984
stream_data = purple_media_get_stream(media, sess_id, name);
985
stream_data->candidates_prepared = TRUE;
987
g_signal_emit(media, purple_media_signals[CANDIDATES_PREPARED],
991
/* callback called when a pair of transport candidates (local and remote)
992
* has been established */
994
purple_media_candidate_pair_established_cb(PurpleMediaBackend *backend,
995
const gchar *sess_id, const gchar *name,
996
PurpleMediaCandidate *local_candidate,
997
PurpleMediaCandidate *remote_candidate,
1000
PurpleMediaStream *stream;
1004
g_return_if_fail(PURPLE_IS_MEDIA(media));
1006
stream = purple_media_get_stream(media, sess_id, name);
1007
id = purple_media_candidate_get_component_id(local_candidate);
1009
iter = stream->active_local_candidates;
1010
for(; iter; iter = g_list_next(iter)) {
1011
PurpleMediaCandidate *c = iter->data;
1012
if (id == purple_media_candidate_get_component_id(c)) {
1014
stream->active_local_candidates =
1015
g_list_delete_link(iter, iter);
1016
stream->active_local_candidates = g_list_prepend(
1017
stream->active_local_candidates,
1018
purple_media_candidate_copy(
1024
stream->active_local_candidates = g_list_prepend(
1025
stream->active_local_candidates,
1026
purple_media_candidate_copy(
1029
id = purple_media_candidate_get_component_id(local_candidate);
1031
iter = stream->active_remote_candidates;
1032
for(; iter; iter = g_list_next(iter)) {
1033
PurpleMediaCandidate *c = iter->data;
1034
if (id == purple_media_candidate_get_component_id(c)) {
1036
stream->active_remote_candidates =
1037
g_list_delete_link(iter, iter);
1038
stream->active_remote_candidates = g_list_prepend(
1039
stream->active_remote_candidates,
1040
purple_media_candidate_copy(
1046
stream->active_remote_candidates = g_list_prepend(
1047
stream->active_remote_candidates,
1048
purple_media_candidate_copy(
1051
purple_debug_info("media", "candidate pair established\n");
1055
purple_media_codecs_changed_cb(PurpleMediaBackend *backend,
1056
const gchar *sess_id, PurpleMedia *media)
1058
g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, sess_id);
1063
purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
1064
const gchar *who, PurpleMediaSessionType type,
1065
gboolean initiator, const gchar *transmitter,
1066
guint num_params, GParameter *params)
1069
PurpleMediaSession *session;
1070
PurpleMediaStream *stream = NULL;
1072
g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1074
if (!purple_media_backend_add_stream(media->priv->backend,
1075
sess_id, who, type, initiator, transmitter,
1076
num_params, params)) {
1077
purple_debug_error("media", "Error adding stream.\n");
1081
session = purple_media_get_session(media, sess_id);
1084
session = g_new0(PurpleMediaSession, 1);
1085
session->id = g_strdup(sess_id);
1086
session->media = media;
1087
session->type = type;
1088
session->initiator = initiator;
1090
purple_media_add_session(media, session);
1091
g_signal_emit(media, purple_media_signals[STATE_CHANGED],
1092
0, PURPLE_MEDIA_STATE_NEW,
1096
if (!g_list_find_custom(media->priv->participants,
1097
who, (GCompareFunc)strcmp)) {
1098
media->priv->participants = g_list_prepend(
1099
media->priv->participants, g_strdup(who));
1101
g_signal_emit_by_name(media, "state-changed",
1102
PURPLE_MEDIA_STATE_NEW, NULL, who);
1105
if (purple_media_get_stream(media, sess_id, who) == NULL) {
1106
stream = purple_media_insert_stream(session, who, initiator);
1108
g_signal_emit(media, purple_media_signals[STATE_CHANGED],
1109
0, PURPLE_MEDIA_STATE_NEW,
1119
PurpleMediaManager *
1120
purple_media_get_manager(PurpleMedia *media)
1122
PurpleMediaManager *ret;
1123
g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1124
g_object_get(media, "manager", &ret, NULL);
1128
PurpleMediaSessionType
1129
purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id)
1132
PurpleMediaSession *session;
1133
g_return_val_if_fail(PURPLE_IS_MEDIA(media), PURPLE_MEDIA_NONE);
1134
session = purple_media_get_session(media, sess_id);
1135
return session->type;
1137
return PURPLE_MEDIA_NONE;
1140
/* XXX: Should wait until codecs-ready is TRUE before using this function */
1142
purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id)
1145
g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1147
return purple_media_backend_get_codecs(media->priv->backend, sess_id);
1154
purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id,
1155
const gchar *participant)
1158
g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1160
return purple_media_backend_get_local_candidates(media->priv->backend,
1161
sess_id, participant);
1168
purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id,
1169
const gchar *participant,
1170
GList *remote_candidates)
1173
PurpleMediaStream *stream;
1175
g_return_if_fail(PURPLE_IS_MEDIA(media));
1176
stream = purple_media_get_stream(media, sess_id, participant);
1178
if (stream == NULL) {
1179
purple_debug_error("media",
1180
"purple_media_add_remote_candidates: "
1181
"couldn't find stream %s %s.\n",
1182
sess_id ? sess_id : "(null)",
1183
participant ? participant : "(null)");
1187
stream->remote_candidates = g_list_concat(stream->remote_candidates,
1188
purple_media_candidate_list_copy(remote_candidates));
1190
purple_media_backend_add_remote_candidates(media->priv->backend,
1191
sess_id, participant, remote_candidates);
1196
purple_media_get_active_local_candidates(PurpleMedia *media,
1197
const gchar *sess_id, const gchar *participant)
1200
PurpleMediaStream *stream;
1201
g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1202
stream = purple_media_get_stream(media, sess_id, participant);
1203
return purple_media_candidate_list_copy(
1204
stream->active_local_candidates);
1211
purple_media_get_active_remote_candidates(PurpleMedia *media,
1212
const gchar *sess_id, const gchar *participant)
1215
PurpleMediaStream *stream;
1216
g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1217
stream = purple_media_get_stream(media, sess_id, participant);
1218
return purple_media_candidate_list_copy(
1219
stream->active_remote_candidates);
1226
purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
1227
const gchar *participant, GList *codecs)
1230
g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1232
return purple_media_backend_set_remote_codecs(media->priv->backend,
1233
sess_id, participant, codecs);
1240
purple_media_candidates_prepared(PurpleMedia *media,
1241
const gchar *session_id, const gchar *participant)
1245
gboolean prepared = TRUE;
1247
g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1249
streams = purple_media_get_streams(media, session_id, participant);
1251
for (; streams; streams = g_list_delete_link(streams, streams)) {
1252
PurpleMediaStream *stream = streams->data;
1253
if (stream->candidates_prepared == FALSE) {
1254
g_list_free(streams);
1267
purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec)
1270
g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1272
return purple_media_backend_set_send_codec(
1273
media->priv->backend, sess_id, codec);
1280
purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id)
1283
g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1285
return purple_media_backend_codecs_ready(
1286
media->priv->backend, sess_id);
1293
purple_media_is_initiator(PurpleMedia *media,
1294
const gchar *sess_id, const gchar *participant)
1297
g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1299
if (sess_id == NULL && participant == NULL)
1300
return media->priv->initiator;
1301
else if (sess_id != NULL && participant == NULL) {
1302
PurpleMediaSession *session =
1303
purple_media_get_session(media, sess_id);
1304
return session != NULL ? session->initiator : FALSE;
1305
} else if (sess_id != NULL && participant != NULL) {
1306
PurpleMediaStream *stream = purple_media_get_stream(
1307
media, sess_id, participant);
1308
return stream != NULL ? stream->initiator : FALSE;
1315
purple_media_accepted(PurpleMedia *media, const gchar *sess_id,
1316
const gchar *participant)
1319
gboolean accepted = TRUE;
1321
g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1323
if (sess_id == NULL && participant == NULL) {
1324
GList *streams = media->priv->streams;
1326
for (; streams; streams = g_list_next(streams)) {
1327
PurpleMediaStream *stream = streams->data;
1328
if (stream->accepted == FALSE) {
1333
} else if (sess_id != NULL && participant == NULL) {
1334
GList *streams = purple_media_get_streams(
1335
media, sess_id, NULL);
1336
for (; streams; streams =
1337
g_list_delete_link(streams, streams)) {
1338
PurpleMediaStream *stream = streams->data;
1339
if (stream->accepted == FALSE) {
1340
g_list_free(streams);
1345
} else if (sess_id != NULL && participant != NULL) {
1346
PurpleMediaStream *stream = purple_media_get_stream(
1347
media, sess_id, participant);
1348
if (stream == NULL || stream->accepted == FALSE)
1358
void purple_media_set_input_volume(PurpleMedia *media,
1359
const gchar *session_id, double level)
1362
g_return_if_fail(PURPLE_IS_MEDIA(media));
1363
g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
1365
purple_media_backend_fs2_set_input_volume(
1366
PURPLE_MEDIA_BACKEND_FS2(
1367
media->priv->backend),
1372
void purple_media_set_output_volume(PurpleMedia *media,
1373
const gchar *session_id, const gchar *participant,
1377
g_return_if_fail(PURPLE_IS_MEDIA(media));
1378
g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend));
1380
purple_media_backend_fs2_set_output_volume(
1381
PURPLE_MEDIA_BACKEND_FS2(
1382
media->priv->backend),
1383
session_id, participant, level);
1388
purple_media_set_output_window(PurpleMedia *media, const gchar *session_id,
1389
const gchar *participant, gulong window_id)
1392
g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
1394
return purple_media_manager_set_output_window(media->priv->manager,
1395
media, session_id, participant, window_id);
1402
purple_media_remove_output_windows(PurpleMedia *media)
1405
GList *iter = media->priv->streams;
1406
for (; iter; iter = g_list_next(iter)) {
1407
PurpleMediaStream *stream = iter->data;
1408
purple_media_manager_remove_output_windows(
1409
media->priv->manager, media,
1410
stream->session->id, stream->participant);
1413
iter = purple_media_get_session_ids(media);
1414
for (; iter; iter = g_list_delete_link(iter, iter)) {
1415
gchar *session_name = iter->data;
1416
purple_media_manager_remove_output_windows(
1417
media->priv->manager, media,
1418
session_name, NULL);
1423
#ifdef USE_GSTREAMER
1425
purple_media_get_tee(PurpleMedia *media,
1426
const gchar *session_id, const gchar *participant)
1429
g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
1431
if (PURPLE_IS_MEDIA_BACKEND_FS2(media->priv->backend))
1432
return purple_media_backend_fs2_get_tee(
1433
PURPLE_MEDIA_BACKEND_FS2(
1434
media->priv->backend),
1435
session_id, participant);
1436
g_return_val_if_reached(NULL);
1441
#endif /* USE_GSTREAMER */