29140
|
1 /**
|
|
2 * @file media.c Media API
|
|
3 * @ingroup core
|
|
4 */
|
|
5
|
|
6 /* purple
|
|
7 *
|
|
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.
|
|
11 *
|
|
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.
|
|
16 *
|
|
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.
|
|
21 *
|
|
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
|
|
25 */
|
|
26
|
|
27 #include <string.h>
|
|
28
|
|
29 #include "internal.h"
|
|
30
|
|
31 #include "account.h"
|
|
32 #include "media.h"
|
|
33 #include "mediamanager.h"
|
|
34 #include "network.h"
|
|
35
|
|
36 #include "debug.h"
|
|
37
|
|
38 #ifdef USE_GSTREAMER
|
|
39 #include "marshallers.h"
|
|
40 #include "media-gst.h"
|
|
41 #endif
|
|
42
|
|
43 #ifdef USE_VV
|
|
44
|
|
45 #include <gst/farsight/fs-conference-iface.h>
|
|
46 #include <gst/farsight/fs-element-added-notifier.h>
|
|
47
|
|
48 /** @copydoc _PurpleMediaSession */
|
|
49 typedef struct _PurpleMediaSession PurpleMediaSession;
|
|
50 /** @copydoc _PurpleMediaStream */
|
|
51 typedef struct _PurpleMediaStream PurpleMediaStream;
|
|
52 /** @copydoc _PurpleMediaClass */
|
|
53 typedef struct _PurpleMediaClass PurpleMediaClass;
|
|
54 /** @copydoc _PurpleMediaPrivate */
|
|
55 typedef struct _PurpleMediaPrivate PurpleMediaPrivate;
|
|
56 /** @copydoc _PurpleMediaCandidateClass */
|
|
57 typedef struct _PurpleMediaCandidateClass PurpleMediaCandidateClass;
|
|
58 /** @copydoc _PurpleMediaCandidatePrivate */
|
|
59 typedef struct _PurpleMediaCandidatePrivate PurpleMediaCandidatePrivate;
|
|
60 /** @copydoc _PurpleMediaCodecClass */
|
|
61 typedef struct _PurpleMediaCodecClass PurpleMediaCodecClass;
|
|
62 /** @copydoc _PurpleMediaCodecPrivate */
|
|
63 typedef struct _PurpleMediaCodecPrivate PurpleMediaCodecPrivate;
|
|
64
|
|
65 /** The media class */
|
|
66 struct _PurpleMediaClass
|
|
67 {
|
|
68 GObjectClass parent_class; /**< The parent class. */
|
|
69 };
|
|
70
|
|
71 /** The media class's private data */
|
|
72 struct _PurpleMedia
|
|
73 {
|
|
74 GObject parent; /**< The parent of this object. */
|
|
75 PurpleMediaPrivate *priv; /**< The private data of this object. */
|
|
76 };
|
|
77
|
|
78 struct _PurpleMediaSession
|
|
79 {
|
|
80 gchar *id;
|
|
81 PurpleMedia *media;
|
|
82 GstElement *src;
|
|
83 GstElement *tee;
|
|
84 FsSession *session;
|
|
85
|
|
86 PurpleMediaSessionType type;
|
|
87 gboolean initiator;
|
|
88 };
|
|
89
|
|
90 struct _PurpleMediaStream
|
|
91 {
|
|
92 PurpleMediaSession *session;
|
|
93 gchar *participant;
|
|
94 FsStream *stream;
|
|
95 GstElement *src;
|
|
96 GstElement *tee;
|
|
97 GstElement *volume;
|
|
98 GstElement *level;
|
|
99
|
|
100 GList *local_candidates;
|
|
101 GList *remote_candidates;
|
|
102
|
|
103 gboolean initiator;
|
|
104 gboolean accepted;
|
|
105 gboolean candidates_prepared;
|
|
106
|
|
107 GList *active_local_candidates;
|
|
108 GList *active_remote_candidates;
|
|
109
|
|
110 guint connected_cb_id;
|
|
111 };
|
|
112 #endif
|
|
113
|
|
114 struct _PurpleMediaPrivate
|
|
115 {
|
|
116 #ifdef USE_VV
|
|
117 PurpleMediaManager *manager;
|
|
118 PurpleAccount *account;
|
|
119 FsConference *conference;
|
|
120 gboolean initiator;
|
|
121 gpointer prpl_data;
|
|
122
|
|
123 GHashTable *sessions; /* PurpleMediaSession table */
|
|
124 GHashTable *participants; /* FsParticipant table */
|
|
125
|
|
126 GList *streams; /* PurpleMediaStream table */
|
|
127
|
|
128 GstElement *confbin;
|
|
129 #else
|
|
130 gpointer dummy;
|
|
131 #endif
|
|
132 };
|
|
133
|
|
134 #ifdef USE_VV
|
|
135 #define PURPLE_MEDIA_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA, PurpleMediaPrivate))
|
|
136 #define PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_CANDIDATE, PurpleMediaCandidatePrivate))
|
|
137 #define PURPLE_MEDIA_CODEC_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_CODEC, PurpleMediaCodecPrivate))
|
|
138
|
|
139 static void purple_media_class_init (PurpleMediaClass *klass);
|
|
140 static void purple_media_init (PurpleMedia *media);
|
|
141 static void purple_media_dispose (GObject *object);
|
|
142 static void purple_media_finalize (GObject *object);
|
|
143 static void purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec);
|
|
144 static void purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec);
|
|
145
|
|
146 static void purple_media_new_local_candidate_cb(FsStream *stream,
|
|
147 FsCandidate *local_candidate, PurpleMediaSession *session);
|
|
148 static void purple_media_candidates_prepared_cb(FsStream *stream,
|
|
149 PurpleMediaSession *session);
|
|
150 static void purple_media_candidate_pair_established_cb(FsStream *stream,
|
|
151 FsCandidate *native_candidate, FsCandidate *remote_candidate,
|
|
152 PurpleMediaSession *session);
|
|
153 static gboolean media_bus_call(GstBus *bus,
|
|
154 GstMessage *msg, PurpleMedia *media);
|
|
155
|
|
156 static GObjectClass *parent_class = NULL;
|
|
157
|
|
158
|
|
159
|
|
160 enum {
|
|
161 S_ERROR,
|
|
162 CANDIDATES_PREPARED,
|
|
163 CODECS_CHANGED,
|
|
164 LEVEL,
|
|
165 NEW_CANDIDATE,
|
|
166 STATE_CHANGED,
|
|
167 STREAM_INFO,
|
|
168 LAST_SIGNAL
|
|
169 };
|
|
170 static guint purple_media_signals[LAST_SIGNAL] = {0};
|
|
171
|
|
172 enum {
|
|
173 PROP_0,
|
|
174 PROP_MANAGER,
|
|
175 PROP_ACCOUNT,
|
|
176 PROP_CONFERENCE,
|
|
177 PROP_INITIATOR,
|
|
178 PROP_PRPL_DATA,
|
|
179 };
|
|
180 #endif
|
|
181
|
|
182
|
|
183 /*
|
|
184 * PurpleMediaElementType
|
|
185 */
|
|
186
|
|
187 GType
|
|
188 purple_media_session_type_get_type()
|
|
189 {
|
|
190 static GType type = 0;
|
|
191 if (type == 0) {
|
|
192 static const GFlagsValue values[] = {
|
|
193 { PURPLE_MEDIA_NONE,
|
|
194 "PURPLE_MEDIA_NONE", "none" },
|
|
195 { PURPLE_MEDIA_RECV_AUDIO,
|
|
196 "PURPLE_MEDIA_RECV_AUDIO", "recv-audio" },
|
|
197 { PURPLE_MEDIA_SEND_AUDIO,
|
|
198 "PURPLE_MEDIA_SEND_AUDIO", "send-audio" },
|
|
199 { PURPLE_MEDIA_RECV_VIDEO,
|
|
200 "PURPLE_MEDIA_RECV_VIDEO", "recv-video" },
|
|
201 { PURPLE_MEDIA_SEND_VIDEO,
|
|
202 "PURPLE_MEDIA_SEND_VIDEO", "send-audio" },
|
|
203 { PURPLE_MEDIA_AUDIO,
|
|
204 "PURPLE_MEDIA_AUDIO", "audio" },
|
|
205 { PURPLE_MEDIA_VIDEO,
|
|
206 "PURPLE_MEDIA_VIDEO", "video" },
|
|
207 { 0, NULL, NULL }
|
|
208 };
|
|
209 type = g_flags_register_static(
|
|
210 "PurpleMediaSessionType", values);
|
|
211 }
|
|
212 return type;
|
|
213 }
|
|
214
|
|
215 GType
|
|
216 purple_media_get_type()
|
|
217 {
|
|
218 #ifdef USE_VV
|
|
219 static GType type = 0;
|
|
220
|
|
221 if (type == 0) {
|
|
222 static const GTypeInfo info = {
|
|
223 sizeof(PurpleMediaClass),
|
|
224 NULL,
|
|
225 NULL,
|
|
226 (GClassInitFunc) purple_media_class_init,
|
|
227 NULL,
|
|
228 NULL,
|
|
229 sizeof(PurpleMedia),
|
|
230 0,
|
|
231 (GInstanceInitFunc) purple_media_init,
|
|
232 NULL
|
|
233 };
|
|
234 type = g_type_register_static(G_TYPE_OBJECT, "PurpleMedia", &info, 0);
|
|
235 }
|
|
236 return type;
|
|
237 #else
|
|
238 return G_TYPE_NONE;
|
|
239 #endif
|
|
240 }
|
|
241
|
|
242 GType
|
|
243 purple_media_state_changed_get_type()
|
|
244 {
|
|
245 static GType type = 0;
|
|
246 if (type == 0) {
|
|
247 static const GEnumValue values[] = {
|
|
248 { PURPLE_MEDIA_STATE_NEW,
|
|
249 "PURPLE_MEDIA_STATE_NEW", "new" },
|
|
250 { PURPLE_MEDIA_STATE_CONNECTED,
|
|
251 "PURPLE_MEDIA_STATE_CONNECTED", "connected" },
|
|
252 { PURPLE_MEDIA_STATE_END,
|
|
253 "PURPLE_MEDIA_STATE_END", "end" },
|
|
254 { 0, NULL, NULL }
|
|
255 };
|
|
256 type = g_enum_register_static("PurpleMediaState", values);
|
|
257 }
|
|
258 return type;
|
|
259 }
|
|
260
|
|
261 GType
|
|
262 purple_media_info_type_get_type()
|
|
263 {
|
|
264 static GType type = 0;
|
|
265 if (type == 0) {
|
|
266 static const GEnumValue values[] = {
|
|
267 { PURPLE_MEDIA_INFO_HANGUP,
|
|
268 "PURPLE_MEDIA_INFO_HANGUP", "hangup" },
|
|
269 { PURPLE_MEDIA_INFO_ACCEPT,
|
|
270 "PURPLE_MEDIA_INFO_ACCEPT", "accept" },
|
|
271 { PURPLE_MEDIA_INFO_REJECT,
|
|
272 "PURPLE_MEDIA_INFO_REJECT", "reject" },
|
|
273 { PURPLE_MEDIA_INFO_MUTE,
|
|
274 "PURPLE_MEDIA_INFO_MUTE", "mute" },
|
|
275 { PURPLE_MEDIA_INFO_UNMUTE,
|
|
276 "PURPLE_MEDIA_INFO_UNMUTE", "unmute" },
|
|
277 { PURPLE_MEDIA_INFO_PAUSE,
|
|
278 "PURPLE_MEDIA_INFO_PAUSE", "pause" },
|
|
279 { PURPLE_MEDIA_INFO_UNPAUSE,
|
|
280 "PURPLE_MEDIA_INFO_UNPAUSE", "unpause" },
|
|
281 { PURPLE_MEDIA_INFO_HOLD,
|
|
282 "PURPLE_MEDIA_INFO_HOLD", "hold" },
|
|
283 { PURPLE_MEDIA_INFO_UNHOLD,
|
|
284 "PURPLE_MEDIA_INFO_HOLD", "unhold" },
|
|
285 { 0, NULL, NULL }
|
|
286 };
|
|
287 type = g_enum_register_static("PurpleMediaInfoType", values);
|
|
288 }
|
|
289 return type;
|
|
290 }
|
|
291
|
|
292 GType
|
|
293 purple_media_caps_get_type()
|
|
294 {
|
|
295 static GType type = 0;
|
|
296 if (type == 0) {
|
|
297 static const GEnumValue values[] = {
|
|
298 { PURPLE_MEDIA_CAPS_NONE,
|
|
299 "PURPLE_MEDIA_CAPS_NONE", "none" },
|
|
300 { PURPLE_MEDIA_CAPS_AUDIO,
|
|
301 "PURPLE_MEDIA_CAPS_AUDIO", "audio" },
|
|
302 { PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION,
|
|
303 "PURPLE_MEDIA_CAPS_AUDIO_SINGLE_DIRECTION",
|
|
304 "audio-single-direction" },
|
|
305 { PURPLE_MEDIA_CAPS_VIDEO,
|
|
306 "PURPLE_MEDIA_CAPS_VIDEO", "video" },
|
|
307 { PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION,
|
|
308 "PURPLE_MEDIA_CAPS_VIDEO_SINGLE_DIRECTION",
|
|
309 "video-single-direction" },
|
|
310 { PURPLE_MEDIA_CAPS_AUDIO_VIDEO,
|
|
311 "PURPLE_MEDIA_CAPS_AUDIO_VIDEO",
|
|
312 "audio-video" },
|
|
313 { PURPLE_MEDIA_CAPS_MODIFY_SESSION,
|
|
314 "PURPLE_MEDIA_CAPS_MODIFY_SESSION",
|
|
315 "modify-session" },
|
|
316 { PURPLE_MEDIA_CAPS_CHANGE_DIRECTION,
|
|
317 "PURPLE_MEDIA_CAPS_CHANGE_DIRECTION",
|
|
318 "change-direction" },
|
|
319 { 0, NULL, NULL }
|
|
320 };
|
|
321 type = g_enum_register_static("PurpleMediaCaps", values);
|
|
322 }
|
|
323 return type;
|
|
324 }
|
|
325
|
|
326 #ifdef USE_VV
|
|
327 static void
|
|
328 purple_media_class_init (PurpleMediaClass *klass)
|
|
329 {
|
|
330 GObjectClass *gobject_class = (GObjectClass*)klass;
|
|
331 parent_class = g_type_class_peek_parent(klass);
|
|
332
|
|
333 gobject_class->dispose = purple_media_dispose;
|
|
334 gobject_class->finalize = purple_media_finalize;
|
|
335 gobject_class->set_property = purple_media_set_property;
|
|
336 gobject_class->get_property = purple_media_get_property;
|
|
337
|
|
338 g_object_class_install_property(gobject_class, PROP_MANAGER,
|
|
339 g_param_spec_object("manager",
|
|
340 "Purple Media Manager",
|
|
341 "The media manager that contains this media session.",
|
|
342 PURPLE_TYPE_MEDIA_MANAGER,
|
|
343 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
|
|
344
|
|
345 g_object_class_install_property(gobject_class, PROP_ACCOUNT,
|
|
346 g_param_spec_pointer("account",
|
|
347 "PurpleAccount",
|
|
348 "The account this media session is on.",
|
|
349 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
|
|
350
|
|
351 g_object_class_install_property(gobject_class, PROP_CONFERENCE,
|
|
352 g_param_spec_object("conference",
|
|
353 "Farsight conference",
|
|
354 "The FsConference associated with this media.",
|
|
355 FS_TYPE_CONFERENCE,
|
|
356 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
|
|
357
|
|
358 g_object_class_install_property(gobject_class, PROP_INITIATOR,
|
|
359 g_param_spec_boolean("initiator",
|
|
360 "initiator",
|
|
361 "If the local user initiated the conference.",
|
|
362 FALSE,
|
|
363 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
|
|
364
|
|
365 g_object_class_install_property(gobject_class, PROP_PRPL_DATA,
|
|
366 g_param_spec_pointer("prpl-data",
|
|
367 "gpointer",
|
|
368 "Data the prpl plugin set on the media session.",
|
|
369 G_PARAM_READWRITE));
|
|
370
|
|
371 purple_media_signals[S_ERROR] = g_signal_new("error", G_TYPE_FROM_CLASS(klass),
|
|
372 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
|
|
373 g_cclosure_marshal_VOID__STRING,
|
|
374 G_TYPE_NONE, 1, G_TYPE_STRING);
|
|
375 purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass),
|
|
376 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
|
|
377 purple_smarshal_VOID__STRING_STRING,
|
|
378 G_TYPE_NONE, 2, G_TYPE_STRING,
|
|
379 G_TYPE_STRING);
|
|
380 purple_media_signals[CODECS_CHANGED] = g_signal_new("codecs-changed", G_TYPE_FROM_CLASS(klass),
|
|
381 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
|
|
382 g_cclosure_marshal_VOID__STRING,
|
|
383 G_TYPE_NONE, 1, G_TYPE_STRING);
|
|
384 purple_media_signals[LEVEL] = g_signal_new("level", G_TYPE_FROM_CLASS(klass),
|
|
385 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
|
|
386 purple_smarshal_VOID__STRING_STRING_DOUBLE,
|
|
387 G_TYPE_NONE, 3, G_TYPE_STRING,
|
|
388 G_TYPE_STRING, G_TYPE_DOUBLE);
|
|
389 purple_media_signals[NEW_CANDIDATE] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass),
|
|
390 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
|
|
391 purple_smarshal_VOID__POINTER_POINTER_OBJECT,
|
|
392 G_TYPE_NONE, 3, G_TYPE_POINTER,
|
|
393 G_TYPE_POINTER, PURPLE_TYPE_MEDIA_CANDIDATE);
|
|
394 purple_media_signals[STATE_CHANGED] = g_signal_new("state-changed", G_TYPE_FROM_CLASS(klass),
|
|
395 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
|
|
396 purple_smarshal_VOID__ENUM_STRING_STRING,
|
|
397 G_TYPE_NONE, 3, PURPLE_MEDIA_TYPE_STATE,
|
|
398 G_TYPE_STRING, G_TYPE_STRING);
|
|
399 purple_media_signals[STREAM_INFO] = g_signal_new("stream-info", G_TYPE_FROM_CLASS(klass),
|
|
400 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
|
|
401 purple_smarshal_VOID__ENUM_STRING_STRING_BOOLEAN,
|
|
402 G_TYPE_NONE, 4, PURPLE_MEDIA_TYPE_INFO_TYPE,
|
|
403 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
|
|
404 g_type_class_add_private(klass, sizeof(PurpleMediaPrivate));
|
|
405 }
|
|
406
|
|
407
|
|
408 static void
|
|
409 purple_media_init (PurpleMedia *media)
|
|
410 {
|
|
411 media->priv = PURPLE_MEDIA_GET_PRIVATE(media);
|
|
412 memset(media->priv, 0, sizeof(*media->priv));
|
|
413 }
|
|
414
|
|
415 static void
|
|
416 purple_media_stream_free(PurpleMediaStream *stream)
|
|
417 {
|
|
418 if (stream == NULL)
|
|
419 return;
|
|
420
|
|
421 /* Remove the connected_cb timeout */
|
|
422 if (stream->connected_cb_id != 0)
|
|
423 purple_timeout_remove(stream->connected_cb_id);
|
|
424
|
|
425 g_free(stream->participant);
|
|
426
|
|
427 if (stream->local_candidates)
|
|
428 fs_candidate_list_destroy(stream->local_candidates);
|
|
429 if (stream->remote_candidates)
|
|
430 fs_candidate_list_destroy(stream->remote_candidates);
|
|
431
|
|
432 if (stream->active_local_candidates)
|
|
433 fs_candidate_list_destroy(stream->active_local_candidates);
|
|
434 if (stream->active_remote_candidates)
|
|
435 fs_candidate_list_destroy(stream->active_remote_candidates);
|
|
436
|
|
437 g_free(stream);
|
|
438 }
|
|
439
|
|
440 static void
|
|
441 purple_media_session_free(PurpleMediaSession *session)
|
|
442 {
|
|
443 if (session == NULL)
|
|
444 return;
|
|
445
|
|
446 g_free(session->id);
|
|
447 g_free(session);
|
|
448 }
|
|
449
|
|
450 static void
|
|
451 purple_media_dispose(GObject *media)
|
|
452 {
|
|
453 PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
|
|
454 GList *iter = NULL;
|
|
455
|
|
456 purple_debug_info("media","purple_media_dispose\n");
|
|
457
|
|
458 purple_media_manager_remove_media(priv->manager, PURPLE_MEDIA(media));
|
|
459
|
|
460 if (priv->confbin) {
|
|
461 gst_element_set_locked_state(priv->confbin, TRUE);
|
|
462 gst_element_set_state(GST_ELEMENT(priv->confbin),
|
|
463 GST_STATE_NULL);
|
|
464 gst_bin_remove(GST_BIN(purple_media_manager_get_pipeline(
|
|
465 priv->manager)), priv->confbin);
|
|
466 priv->confbin = NULL;
|
|
467 priv->conference = NULL;
|
|
468 }
|
|
469
|
|
470 for (iter = priv->streams; iter; iter = g_list_next(iter)) {
|
|
471 PurpleMediaStream *stream = iter->data;
|
|
472 if (stream->stream) {
|
|
473 g_object_unref(stream->stream);
|
|
474 stream->stream = NULL;
|
|
475 }
|
|
476 }
|
|
477
|
|
478 if (priv->sessions) {
|
|
479 GList *sessions = g_hash_table_get_values(priv->sessions);
|
|
480 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
|
|
481 PurpleMediaSession *session = sessions->data;
|
|
482 if (session->session) {
|
|
483 g_object_unref(session->session);
|
|
484 session->session = NULL;
|
|
485 }
|
|
486 }
|
|
487 }
|
|
488
|
|
489 if (priv->participants) {
|
|
490 GList *participants = g_hash_table_get_values(priv->participants);
|
|
491 for (; participants; participants = g_list_delete_link(participants, participants))
|
|
492 g_object_unref(participants->data);
|
|
493 }
|
|
494
|
|
495 if (priv->manager) {
|
|
496 GstElement *pipeline = purple_media_manager_get_pipeline(
|
|
497 priv->manager);
|
|
498 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
|
|
499 g_signal_handlers_disconnect_matched(G_OBJECT(bus),
|
|
500 G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA,
|
|
501 0, 0, 0, media_bus_call, media);
|
|
502 gst_object_unref(bus);
|
|
503
|
|
504 g_object_unref(priv->manager);
|
|
505 priv->manager = NULL;
|
|
506 }
|
|
507
|
|
508 G_OBJECT_CLASS(parent_class)->dispose(media);
|
|
509 }
|
|
510
|
|
511 static void
|
|
512 purple_media_finalize(GObject *media)
|
|
513 {
|
|
514 PurpleMediaPrivate *priv = PURPLE_MEDIA_GET_PRIVATE(media);
|
|
515 purple_debug_info("media","purple_media_finalize\n");
|
|
516
|
|
517 for (; priv->streams; priv->streams = g_list_delete_link(priv->streams, priv->streams))
|
|
518 purple_media_stream_free(priv->streams->data);
|
|
519
|
|
520 if (priv->sessions) {
|
|
521 GList *sessions = g_hash_table_get_values(priv->sessions);
|
|
522 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
|
|
523 purple_media_session_free(sessions->data);
|
|
524 }
|
|
525 g_hash_table_destroy(priv->sessions);
|
|
526 }
|
|
527
|
|
528 G_OBJECT_CLASS(parent_class)->finalize(media);
|
|
529 }
|
|
530
|
|
531 static void
|
|
532 purple_media_setup_pipeline(PurpleMedia *media)
|
|
533 {
|
|
534 GstBus *bus;
|
|
535 gchar *name;
|
|
536 GstElement *pipeline;
|
|
537
|
|
538 if (media->priv->conference == NULL || media->priv->manager == NULL)
|
|
539 return;
|
|
540
|
|
541 pipeline = purple_media_manager_get_pipeline(media->priv->manager);
|
|
542
|
|
543 name = g_strdup_printf("conf_%p",
|
|
544 media->priv->conference);
|
|
545 media->priv->confbin = gst_bin_new(name);
|
|
546 g_free(name);
|
|
547
|
|
548 bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
|
|
549 g_signal_connect(G_OBJECT(bus), "message",
|
|
550 G_CALLBACK(media_bus_call), media);
|
|
551 gst_object_unref(bus);
|
|
552
|
|
553 gst_bin_add(GST_BIN(pipeline),
|
|
554 media->priv->confbin);
|
|
555 gst_bin_add(GST_BIN(media->priv->confbin),
|
|
556 GST_ELEMENT(media->priv->conference));
|
|
557 gst_element_set_state(GST_ELEMENT(media->priv->confbin),
|
|
558 GST_STATE_PLAYING);
|
|
559 }
|
|
560
|
|
561 static void
|
|
562 purple_media_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
|
|
563 {
|
|
564 PurpleMedia *media;
|
|
565 g_return_if_fail(PURPLE_IS_MEDIA(object));
|
|
566
|
|
567 media = PURPLE_MEDIA(object);
|
|
568
|
|
569 switch (prop_id) {
|
|
570 case PROP_MANAGER:
|
|
571 media->priv->manager = g_value_get_object(value);
|
|
572 g_object_ref(media->priv->manager);
|
|
573
|
|
574 purple_media_setup_pipeline(media);
|
|
575 break;
|
|
576 case PROP_ACCOUNT:
|
|
577 media->priv->account = g_value_get_pointer(value);
|
|
578 break;
|
|
579 case PROP_CONFERENCE: {
|
|
580 if (media->priv->conference)
|
|
581 gst_object_unref(media->priv->conference);
|
|
582 media->priv->conference = g_value_get_object(value);
|
|
583 gst_object_ref(media->priv->conference);
|
|
584
|
|
585 purple_media_setup_pipeline(media);
|
|
586 break;
|
|
587 }
|
|
588 case PROP_INITIATOR:
|
|
589 media->priv->initiator = g_value_get_boolean(value);
|
|
590 break;
|
|
591 case PROP_PRPL_DATA:
|
|
592 media->priv->prpl_data = g_value_get_pointer(value);
|
|
593 break;
|
|
594 default:
|
|
595 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
596 break;
|
|
597 }
|
|
598 }
|
|
599
|
|
600 static void
|
|
601 purple_media_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
|
|
602 {
|
|
603 PurpleMedia *media;
|
|
604 g_return_if_fail(PURPLE_IS_MEDIA(object));
|
|
605
|
|
606 media = PURPLE_MEDIA(object);
|
|
607
|
|
608 switch (prop_id) {
|
|
609 case PROP_MANAGER:
|
|
610 g_value_set_object(value, media->priv->manager);
|
|
611 break;
|
|
612 case PROP_ACCOUNT:
|
|
613 g_value_set_pointer(value, media->priv->account);
|
|
614 break;
|
|
615 case PROP_CONFERENCE:
|
|
616 g_value_set_object(value, media->priv->conference);
|
|
617 break;
|
|
618 case PROP_INITIATOR:
|
|
619 g_value_set_boolean(value, media->priv->initiator);
|
|
620 break;
|
|
621 case PROP_PRPL_DATA:
|
|
622 g_value_set_pointer(value, media->priv->prpl_data);
|
|
623 break;
|
|
624 default:
|
|
625 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
626 break;
|
|
627 }
|
|
628
|
|
629 }
|
|
630 #endif
|
|
631
|
|
632 /*
|
|
633 * PurpleMediaCandidateType
|
|
634 */
|
|
635
|
|
636 GType
|
|
637 purple_media_candidate_type_get_type()
|
|
638 {
|
|
639 static GType type = 0;
|
|
640 if (type == 0) {
|
|
641 static const GEnumValue values[] = {
|
|
642 { PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
|
|
643 "PURPLE_MEDIA_CANDIDATE_TYPE_HOST",
|
|
644 "host" },
|
|
645 { PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX,
|
|
646 "PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX",
|
|
647 "srflx" },
|
|
648 { PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX,
|
|
649 "PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX",
|
|
650 "prflx" },
|
|
651 { PURPLE_MEDIA_CANDIDATE_TYPE_RELAY,
|
|
652 "PPURPLE_MEDIA_CANDIDATE_TYPE_RELAY",
|
|
653 "relay" },
|
|
654 { PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST,
|
|
655 "PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST",
|
|
656 "multicast" },
|
|
657 { 0, NULL, NULL }
|
|
658 };
|
|
659 type = g_enum_register_static("PurpleMediaCandidateType",
|
|
660 values);
|
|
661 }
|
|
662 return type;
|
|
663 }
|
|
664
|
|
665 /*
|
|
666 * PurpleMediaNetworkProtocol
|
|
667 */
|
|
668
|
|
669 GType
|
|
670 purple_media_network_protocol_get_type()
|
|
671 {
|
|
672 static GType type = 0;
|
|
673 if (type == 0) {
|
|
674 static const GEnumValue values[] = {
|
|
675 { PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
|
|
676 "PURPLE_MEDIA_NETWORK_PROTOCOL_UDP",
|
|
677 "udp" },
|
|
678 { PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
|
|
679 "PURPLE_MEDIA_NETWORK_PROTOCOL_TCP",
|
|
680 "tcp" },
|
|
681 { 0, NULL, NULL }
|
|
682 };
|
|
683 type = g_enum_register_static("PurpleMediaNetworkProtocol",
|
|
684 values);
|
|
685 }
|
|
686 return type;
|
|
687 }
|
|
688
|
|
689 /*
|
|
690 * PurpleMediaCandidate
|
|
691 */
|
|
692
|
|
693 struct _PurpleMediaCandidateClass
|
|
694 {
|
|
695 GObjectClass parent_class;
|
|
696 };
|
|
697
|
|
698 struct _PurpleMediaCandidate
|
|
699 {
|
|
700 GObject parent;
|
|
701 };
|
|
702
|
|
703 #ifdef USE_VV
|
|
704 struct _PurpleMediaCandidatePrivate
|
|
705 {
|
|
706 gchar *foundation;
|
|
707 guint component_id;
|
|
708 gchar *ip;
|
|
709 guint16 port;
|
|
710 gchar *base_ip;
|
|
711 guint16 base_port;
|
|
712 PurpleMediaNetworkProtocol proto;
|
|
713 guint32 priority;
|
|
714 PurpleMediaCandidateType type;
|
|
715 gchar *username;
|
|
716 gchar *password;
|
|
717 guint ttl;
|
|
718 };
|
|
719
|
|
720 enum {
|
|
721 PROP_CANDIDATE_0,
|
|
722 PROP_FOUNDATION,
|
|
723 PROP_COMPONENT_ID,
|
|
724 PROP_IP,
|
|
725 PROP_PORT,
|
|
726 PROP_BASE_IP,
|
|
727 PROP_BASE_PORT,
|
|
728 PROP_PROTOCOL,
|
|
729 PROP_PRIORITY,
|
|
730 PROP_TYPE,
|
|
731 PROP_USERNAME,
|
|
732 PROP_PASSWORD,
|
|
733 PROP_TTL,
|
|
734 };
|
|
735
|
|
736 static void
|
|
737 purple_media_candidate_init(PurpleMediaCandidate *info)
|
|
738 {
|
|
739 PurpleMediaCandidatePrivate *priv =
|
|
740 PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info);
|
|
741 priv->foundation = NULL;
|
|
742 priv->component_id = 0;
|
|
743 priv->ip = NULL;
|
|
744 priv->port = 0;
|
|
745 priv->base_ip = NULL;
|
|
746 priv->proto = PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
|
|
747 priv->priority = 0;
|
|
748 priv->type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
|
|
749 priv->username = NULL;
|
|
750 priv->password = NULL;
|
|
751 priv->ttl = 0;
|
|
752 }
|
|
753
|
|
754 static void
|
|
755 purple_media_candidate_finalize(GObject *info)
|
|
756 {
|
|
757 PurpleMediaCandidatePrivate *priv =
|
|
758 PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(info);
|
|
759
|
|
760 g_free(priv->foundation);
|
|
761 g_free(priv->ip);
|
|
762 g_free(priv->base_ip);
|
|
763 g_free(priv->username);
|
|
764 g_free(priv->password);
|
|
765 }
|
|
766
|
|
767 static void
|
|
768 purple_media_candidate_set_property (GObject *object, guint prop_id,
|
|
769 const GValue *value, GParamSpec *pspec)
|
|
770 {
|
|
771 PurpleMediaCandidatePrivate *priv;
|
|
772 g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object));
|
|
773
|
|
774 priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object);
|
|
775
|
|
776 switch (prop_id) {
|
|
777 case PROP_FOUNDATION:
|
|
778 g_free(priv->foundation);
|
|
779 priv->foundation = g_value_dup_string(value);
|
|
780 break;
|
|
781 case PROP_COMPONENT_ID:
|
|
782 priv->component_id = g_value_get_uint(value);
|
|
783 break;
|
|
784 case PROP_IP:
|
|
785 g_free(priv->ip);
|
|
786 priv->ip = g_value_dup_string(value);
|
|
787 break;
|
|
788 case PROP_PORT:
|
|
789 priv->port = g_value_get_uint(value);
|
|
790 break;
|
|
791 case PROP_BASE_IP:
|
|
792 g_free(priv->base_ip);
|
|
793 priv->base_ip = g_value_dup_string(value);
|
|
794 break;
|
|
795 case PROP_BASE_PORT:
|
|
796 priv->base_port = g_value_get_uint(value);
|
|
797 break;
|
|
798 case PROP_PROTOCOL:
|
|
799 priv->proto = g_value_get_enum(value);
|
|
800 break;
|
|
801 case PROP_PRIORITY:
|
|
802 priv->priority = g_value_get_uint(value);
|
|
803 break;
|
|
804 case PROP_TYPE:
|
|
805 priv->type = g_value_get_enum(value);
|
|
806 break;
|
|
807 case PROP_USERNAME:
|
|
808 g_free(priv->username);
|
|
809 priv->username = g_value_dup_string(value);
|
|
810 break;
|
|
811 case PROP_PASSWORD:
|
|
812 g_free(priv->password);
|
|
813 priv->password = g_value_dup_string(value);
|
|
814 break;
|
|
815 case PROP_TTL:
|
|
816 priv->ttl = g_value_get_uint(value);
|
|
817 break;
|
|
818 default:
|
|
819 G_OBJECT_WARN_INVALID_PROPERTY_ID(
|
|
820 object, prop_id, pspec);
|
|
821 break;
|
|
822 }
|
|
823 }
|
|
824
|
|
825 static void
|
|
826 purple_media_candidate_get_property (GObject *object, guint prop_id,
|
|
827 GValue *value, GParamSpec *pspec)
|
|
828 {
|
|
829 PurpleMediaCandidatePrivate *priv;
|
|
830 g_return_if_fail(PURPLE_IS_MEDIA_CANDIDATE(object));
|
|
831
|
|
832 priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(object);
|
|
833
|
|
834 switch (prop_id) {
|
|
835 case PROP_FOUNDATION:
|
|
836 g_value_set_string(value, priv->foundation);
|
|
837 break;
|
|
838 case PROP_COMPONENT_ID:
|
|
839 g_value_set_uint(value, priv->component_id);
|
|
840 break;
|
|
841 case PROP_IP:
|
|
842 g_value_set_string(value, priv->ip);
|
|
843 break;
|
|
844 case PROP_PORT:
|
|
845 g_value_set_uint(value, priv->port);
|
|
846 break;
|
|
847 case PROP_BASE_IP:
|
|
848 g_value_set_string(value, priv->base_ip);
|
|
849 break;
|
|
850 case PROP_BASE_PORT:
|
|
851 g_value_set_uint(value, priv->base_port);
|
|
852 break;
|
|
853 case PROP_PROTOCOL:
|
|
854 g_value_set_enum(value, priv->proto);
|
|
855 break;
|
|
856 case PROP_PRIORITY:
|
|
857 g_value_set_uint(value, priv->priority);
|
|
858 break;
|
|
859 case PROP_TYPE:
|
|
860 g_value_set_enum(value, priv->type);
|
|
861 break;
|
|
862 case PROP_USERNAME:
|
|
863 g_value_set_string(value, priv->username);
|
|
864 break;
|
|
865 case PROP_PASSWORD:
|
|
866 g_value_set_string(value, priv->password);
|
|
867 break;
|
|
868 case PROP_TTL:
|
|
869 g_value_set_uint(value, priv->ttl);
|
|
870 break;
|
|
871 default:
|
|
872 G_OBJECT_WARN_INVALID_PROPERTY_ID(
|
|
873 object, prop_id, pspec);
|
|
874 break;
|
|
875 }
|
|
876 }
|
|
877
|
|
878 static void
|
|
879 purple_media_candidate_class_init(PurpleMediaCandidateClass *klass)
|
|
880 {
|
|
881 GObjectClass *gobject_class = (GObjectClass*)klass;
|
|
882
|
|
883 gobject_class->finalize = purple_media_candidate_finalize;
|
|
884 gobject_class->set_property = purple_media_candidate_set_property;
|
|
885 gobject_class->get_property = purple_media_candidate_get_property;
|
|
886
|
|
887 g_object_class_install_property(gobject_class, PROP_FOUNDATION,
|
|
888 g_param_spec_string("foundation",
|
|
889 "Foundation",
|
|
890 "The foundation of the candidate.",
|
|
891 NULL,
|
|
892 G_PARAM_READWRITE));
|
|
893
|
|
894 g_object_class_install_property(gobject_class, PROP_COMPONENT_ID,
|
|
895 g_param_spec_uint("component-id",
|
|
896 "Component ID",
|
|
897 "The component id of the candidate.",
|
|
898 0, G_MAXUINT, 0,
|
|
899 G_PARAM_READWRITE));
|
|
900
|
|
901 g_object_class_install_property(gobject_class, PROP_IP,
|
|
902 g_param_spec_string("ip",
|
|
903 "IP Address",
|
|
904 "The IP address of the candidate.",
|
|
905 NULL,
|
|
906 G_PARAM_READWRITE));
|
|
907
|
|
908 g_object_class_install_property(gobject_class, PROP_PORT,
|
|
909 g_param_spec_uint("port",
|
|
910 "Port",
|
|
911 "The port of the candidate.",
|
|
912 0, G_MAXUINT16, 0,
|
|
913 G_PARAM_READWRITE));
|
|
914
|
|
915 g_object_class_install_property(gobject_class, PROP_BASE_IP,
|
|
916 g_param_spec_string("base-ip",
|
|
917 "Base IP",
|
|
918 "The internal IP address of the candidate.",
|
|
919 NULL,
|
|
920 G_PARAM_READWRITE));
|
|
921
|
|
922 g_object_class_install_property(gobject_class, PROP_BASE_PORT,
|
|
923 g_param_spec_uint("base-port",
|
|
924 "Base Port",
|
|
925 "The internal port of the candidate.",
|
|
926 0, G_MAXUINT16, 0,
|
|
927 G_PARAM_READWRITE));
|
|
928
|
|
929 g_object_class_install_property(gobject_class, PROP_PROTOCOL,
|
|
930 g_param_spec_enum("protocol",
|
|
931 "Protocol",
|
|
932 "The protocol of the candidate.",
|
|
933 PURPLE_TYPE_MEDIA_NETWORK_PROTOCOL,
|
|
934 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP,
|
|
935 G_PARAM_READWRITE));
|
|
936
|
|
937 g_object_class_install_property(gobject_class, PROP_PRIORITY,
|
|
938 g_param_spec_uint("priority",
|
|
939 "Priority",
|
|
940 "The priority of the candidate.",
|
|
941 0, G_MAXUINT32, 0,
|
|
942 G_PARAM_READWRITE));
|
|
943
|
|
944 g_object_class_install_property(gobject_class, PROP_TYPE,
|
|
945 g_param_spec_enum("type",
|
|
946 "Type",
|
|
947 "The type of the candidate.",
|
|
948 PURPLE_TYPE_MEDIA_CANDIDATE_TYPE,
|
|
949 PURPLE_MEDIA_CANDIDATE_TYPE_HOST,
|
|
950 G_PARAM_READWRITE));
|
|
951
|
|
952 g_object_class_install_property(gobject_class, PROP_USERNAME,
|
|
953 g_param_spec_string("username",
|
|
954 "Username",
|
|
955 "The username used to connect to the candidate.",
|
|
956 NULL,
|
|
957 G_PARAM_READWRITE));
|
|
958
|
|
959 g_object_class_install_property(gobject_class, PROP_PASSWORD,
|
|
960 g_param_spec_string("password",
|
|
961 "Password",
|
|
962 "The password use to connect to the candidate.",
|
|
963 NULL,
|
|
964 G_PARAM_READWRITE));
|
|
965
|
|
966 g_object_class_install_property(gobject_class, PROP_TTL,
|
|
967 g_param_spec_uint("ttl",
|
|
968 "TTL",
|
|
969 "The TTL of the candidate.",
|
|
970 0, G_MAXUINT, 0,
|
|
971 G_PARAM_READWRITE));
|
|
972
|
|
973 g_type_class_add_private(klass, sizeof(PurpleMediaCandidatePrivate));
|
|
974 }
|
|
975
|
|
976 G_DEFINE_TYPE(PurpleMediaCandidate,
|
|
977 purple_media_candidate, G_TYPE_OBJECT);
|
|
978 #else
|
|
979 GType
|
|
980 purple_media_candidate_get_type()
|
|
981 {
|
|
982 return G_TYPE_NONE;
|
|
983 }
|
|
984 #endif
|
|
985
|
|
986 PurpleMediaCandidate *
|
|
987 purple_media_candidate_new(const gchar *foundation, guint component_id,
|
|
988 PurpleMediaCandidateType type,
|
|
989 PurpleMediaNetworkProtocol proto,
|
|
990 const gchar *ip, guint port)
|
|
991 {
|
|
992 return g_object_new(PURPLE_TYPE_MEDIA_CANDIDATE,
|
|
993 "foundation", foundation,
|
|
994 "component-id", component_id,
|
|
995 "type", type,
|
|
996 "protocol", proto,
|
|
997 "ip", ip,
|
|
998 "port", port, NULL);
|
|
999 }
|
|
1000
|
|
1001 static PurpleMediaCandidate *
|
|
1002 purple_media_candidate_copy(PurpleMediaCandidate *candidate)
|
|
1003 {
|
|
1004 #ifdef USE_VV
|
|
1005 PurpleMediaCandidatePrivate *priv;
|
|
1006 PurpleMediaCandidate *new_candidate;
|
|
1007
|
|
1008 if (candidate == NULL)
|
|
1009 return NULL;
|
|
1010
|
|
1011 priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(candidate);
|
|
1012
|
|
1013 new_candidate = purple_media_candidate_new(priv->foundation,
|
|
1014 priv->component_id, priv->type, priv->proto,
|
|
1015 priv->ip, priv->port);
|
|
1016 g_object_set(new_candidate,
|
|
1017 "base-ip", priv->base_ip,
|
|
1018 "base-port", priv->base_port,
|
|
1019 "priority", priv->priority,
|
|
1020 "username", priv->username,
|
|
1021 "password", priv->password,
|
|
1022 "ttl", priv->ttl, NULL);
|
|
1023 return new_candidate;
|
|
1024 #else
|
|
1025 return NULL;
|
|
1026 #endif
|
|
1027 }
|
|
1028
|
|
1029 #ifdef USE_VV
|
|
1030 static FsCandidate *
|
|
1031 purple_media_candidate_to_fs(PurpleMediaCandidate *candidate)
|
|
1032 {
|
|
1033 PurpleMediaCandidatePrivate *priv;
|
|
1034 FsCandidate *fscandidate;
|
|
1035
|
|
1036 if (candidate == NULL)
|
|
1037 return NULL;
|
|
1038
|
|
1039 priv = PURPLE_MEDIA_CANDIDATE_GET_PRIVATE(candidate);
|
|
1040
|
|
1041 fscandidate = fs_candidate_new(priv->foundation,
|
|
1042 priv->component_id, priv->type,
|
|
1043 priv->proto, priv->ip, priv->port);
|
|
1044
|
|
1045 fscandidate->base_ip = g_strdup(priv->base_ip);
|
|
1046 fscandidate->base_port = priv->base_port;
|
|
1047 fscandidate->priority = priv->priority;
|
|
1048 fscandidate->username = g_strdup(priv->username);
|
|
1049 fscandidate->password = g_strdup(priv->password);
|
|
1050 fscandidate->ttl = priv->ttl;
|
|
1051 return fscandidate;
|
|
1052 }
|
|
1053
|
|
1054 static PurpleMediaCandidate *
|
|
1055 purple_media_candidate_from_fs(FsCandidate *fscandidate)
|
|
1056 {
|
|
1057 PurpleMediaCandidate *candidate;
|
|
1058
|
|
1059 if (fscandidate == NULL)
|
|
1060 return NULL;
|
|
1061
|
|
1062 candidate = purple_media_candidate_new(fscandidate->foundation,
|
|
1063 fscandidate->component_id, fscandidate->type,
|
|
1064 fscandidate->proto, fscandidate->ip, fscandidate->port);
|
|
1065 g_object_set(candidate,
|
|
1066 "base-ip", fscandidate->base_ip,
|
|
1067 "base-port", fscandidate->base_port,
|
|
1068 "priority", fscandidate->priority,
|
|
1069 "username", fscandidate->username,
|
|
1070 "password", fscandidate->password,
|
|
1071 "ttl", fscandidate->ttl, NULL);
|
|
1072 return candidate;
|
|
1073 }
|
|
1074
|
|
1075 static GList *
|
|
1076 purple_media_candidate_list_from_fs(GList *candidates)
|
|
1077 {
|
|
1078 GList *new_list = NULL;
|
|
1079
|
|
1080 for (; candidates; candidates = g_list_next(candidates)) {
|
|
1081 new_list = g_list_prepend(new_list,
|
|
1082 purple_media_candidate_from_fs(
|
|
1083 candidates->data));
|
|
1084 }
|
|
1085
|
|
1086 new_list = g_list_reverse(new_list);
|
|
1087 return new_list;
|
|
1088 }
|
|
1089
|
|
1090 static GList *
|
|
1091 purple_media_candidate_list_to_fs(GList *candidates)
|
|
1092 {
|
|
1093 GList *new_list = NULL;
|
|
1094
|
|
1095 for (; candidates; candidates = g_list_next(candidates)) {
|
|
1096 new_list = g_list_prepend(new_list,
|
|
1097 purple_media_candidate_to_fs(
|
|
1098 candidates->data));
|
|
1099 }
|
|
1100
|
|
1101 new_list = g_list_reverse(new_list);
|
|
1102 return new_list;
|
|
1103 }
|
|
1104 #endif
|
|
1105
|
|
1106 GList *
|
|
1107 purple_media_candidate_list_copy(GList *candidates)
|
|
1108 {
|
|
1109 GList *new_list = NULL;
|
|
1110
|
|
1111 for (; candidates; candidates = g_list_next(candidates)) {
|
|
1112 new_list = g_list_prepend(new_list,
|
|
1113 purple_media_candidate_copy(candidates->data));
|
|
1114 }
|
|
1115
|
|
1116 new_list = g_list_reverse(new_list);
|
|
1117 return new_list;
|
|
1118 }
|
|
1119
|
|
1120 void
|
|
1121 purple_media_candidate_list_free(GList *candidates)
|
|
1122 {
|
|
1123 for (; candidates; candidates =
|
|
1124 g_list_delete_link(candidates, candidates)) {
|
|
1125 g_object_unref(candidates->data);
|
|
1126 }
|
|
1127 }
|
|
1128
|
|
1129 gchar *
|
|
1130 purple_media_candidate_get_foundation(PurpleMediaCandidate *candidate)
|
|
1131 {
|
|
1132 gchar *foundation;
|
|
1133 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
|
|
1134 g_object_get(candidate, "foundation", &foundation, NULL);
|
|
1135 return foundation;
|
|
1136 }
|
|
1137
|
|
1138 guint
|
|
1139 purple_media_candidate_get_component_id(PurpleMediaCandidate *candidate)
|
|
1140 {
|
|
1141 guint component_id;
|
|
1142 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
|
|
1143 g_object_get(candidate, "component-id", &component_id, NULL);
|
|
1144 return component_id;
|
|
1145 }
|
|
1146
|
|
1147 gchar *
|
|
1148 purple_media_candidate_get_ip(PurpleMediaCandidate *candidate)
|
|
1149 {
|
|
1150 gchar *ip;
|
|
1151 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
|
|
1152 g_object_get(candidate, "ip", &ip, NULL);
|
|
1153 return ip;
|
|
1154 }
|
|
1155
|
|
1156 guint16
|
|
1157 purple_media_candidate_get_port(PurpleMediaCandidate *candidate)
|
|
1158 {
|
|
1159 guint port;
|
|
1160 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
|
|
1161 g_object_get(candidate, "port", &port, NULL);
|
|
1162 return port;
|
|
1163 }
|
|
1164
|
|
1165 gchar *
|
|
1166 purple_media_candidate_get_base_ip(PurpleMediaCandidate *candidate)
|
|
1167 {
|
|
1168 gchar *base_ip;
|
|
1169 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
|
|
1170 g_object_get(candidate, "base-ip", &base_ip, NULL);
|
|
1171 return base_ip;
|
|
1172 }
|
|
1173
|
|
1174 guint16
|
|
1175 purple_media_candidate_get_base_port(PurpleMediaCandidate *candidate)
|
|
1176 {
|
|
1177 guint base_port;
|
|
1178 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
|
|
1179 g_object_get(candidate, "base_port", &base_port, NULL);
|
|
1180 return base_port;
|
|
1181 }
|
|
1182
|
|
1183 PurpleMediaNetworkProtocol
|
|
1184 purple_media_candidate_get_protocol(PurpleMediaCandidate *candidate)
|
|
1185 {
|
|
1186 PurpleMediaNetworkProtocol protocol;
|
|
1187 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate),
|
|
1188 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP);
|
|
1189 g_object_get(candidate, "protocol", &protocol, NULL);
|
|
1190 return protocol;
|
|
1191 }
|
|
1192
|
|
1193 guint32
|
|
1194 purple_media_candidate_get_priority(PurpleMediaCandidate *candidate)
|
|
1195 {
|
|
1196 guint priority;
|
|
1197 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
|
|
1198 g_object_get(candidate, "priority", &priority, NULL);
|
|
1199 return priority;
|
|
1200 }
|
|
1201
|
|
1202 PurpleMediaCandidateType
|
|
1203 purple_media_candidate_get_candidate_type(PurpleMediaCandidate *candidate)
|
|
1204 {
|
|
1205 PurpleMediaCandidateType type;
|
|
1206 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate),
|
|
1207 PURPLE_MEDIA_CANDIDATE_TYPE_HOST);
|
|
1208 g_object_get(candidate, "type", &type, NULL);
|
|
1209 return type;
|
|
1210 }
|
|
1211
|
|
1212 gchar *
|
|
1213 purple_media_candidate_get_username(PurpleMediaCandidate *candidate)
|
|
1214 {
|
|
1215 gchar *username;
|
|
1216 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
|
|
1217 g_object_get(candidate, "username", &username, NULL);
|
|
1218 return username;
|
|
1219 }
|
|
1220
|
|
1221 gchar *
|
|
1222 purple_media_candidate_get_password(PurpleMediaCandidate *candidate)
|
|
1223 {
|
|
1224 gchar *password;
|
|
1225 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), NULL);
|
|
1226 g_object_get(candidate, "password", &password, NULL);
|
|
1227 return password;
|
|
1228 }
|
|
1229
|
|
1230 guint
|
|
1231 purple_media_candidate_get_ttl(PurpleMediaCandidate *candidate)
|
|
1232 {
|
|
1233 guint ttl;
|
|
1234 g_return_val_if_fail(PURPLE_IS_MEDIA_CANDIDATE(candidate), 0);
|
|
1235 g_object_get(candidate, "ttl", &ttl, NULL);
|
|
1236 return ttl;
|
|
1237 }
|
|
1238
|
|
1239 #ifdef USE_VV
|
|
1240 static FsMediaType
|
|
1241 purple_media_to_fs_media_type(PurpleMediaSessionType type)
|
|
1242 {
|
|
1243 if (type & PURPLE_MEDIA_AUDIO)
|
|
1244 return FS_MEDIA_TYPE_AUDIO;
|
|
1245 else if (type & PURPLE_MEDIA_VIDEO)
|
|
1246 return FS_MEDIA_TYPE_VIDEO;
|
|
1247 else
|
|
1248 return 0;
|
|
1249 }
|
|
1250
|
|
1251 static FsStreamDirection
|
|
1252 purple_media_to_fs_stream_direction(PurpleMediaSessionType type)
|
|
1253 {
|
|
1254 if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_AUDIO ||
|
|
1255 (type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO)
|
|
1256 return FS_DIRECTION_BOTH;
|
|
1257 else if ((type & PURPLE_MEDIA_SEND_AUDIO) ||
|
|
1258 (type & PURPLE_MEDIA_SEND_VIDEO))
|
|
1259 return FS_DIRECTION_SEND;
|
|
1260 else if ((type & PURPLE_MEDIA_RECV_AUDIO) ||
|
|
1261 (type & PURPLE_MEDIA_RECV_VIDEO))
|
|
1262 return FS_DIRECTION_RECV;
|
|
1263 else
|
|
1264 return FS_DIRECTION_NONE;
|
|
1265 }
|
|
1266
|
|
1267 static PurpleMediaSessionType
|
|
1268 purple_media_from_fs(FsMediaType type, FsStreamDirection direction)
|
|
1269 {
|
|
1270 PurpleMediaSessionType result = PURPLE_MEDIA_NONE;
|
|
1271 if (type == FS_MEDIA_TYPE_AUDIO) {
|
|
1272 if (direction & FS_DIRECTION_SEND)
|
|
1273 result |= PURPLE_MEDIA_SEND_AUDIO;
|
|
1274 if (direction & FS_DIRECTION_RECV)
|
|
1275 result |= PURPLE_MEDIA_RECV_AUDIO;
|
|
1276 } else if (type == FS_MEDIA_TYPE_VIDEO) {
|
|
1277 if (direction & FS_DIRECTION_SEND)
|
|
1278 result |= PURPLE_MEDIA_SEND_VIDEO;
|
|
1279 if (direction & FS_DIRECTION_RECV)
|
|
1280 result |= PURPLE_MEDIA_RECV_VIDEO;
|
|
1281 }
|
|
1282 return result;
|
|
1283 }
|
|
1284 #endif
|
|
1285
|
|
1286 /*
|
|
1287 * PurpleMediaCodec
|
|
1288 */
|
|
1289
|
|
1290 struct _PurpleMediaCodecClass
|
|
1291 {
|
|
1292 GObjectClass parent_class;
|
|
1293 };
|
|
1294
|
|
1295 struct _PurpleMediaCodec
|
|
1296 {
|
|
1297 GObject parent;
|
|
1298 };
|
|
1299
|
|
1300 #ifdef USE_VV
|
|
1301 struct _PurpleMediaCodecPrivate
|
|
1302 {
|
|
1303 gint id;
|
|
1304 char *encoding_name;
|
|
1305 PurpleMediaSessionType media_type;
|
|
1306 guint clock_rate;
|
|
1307 guint channels;
|
|
1308 GList *optional_params;
|
|
1309 };
|
|
1310
|
|
1311 enum {
|
|
1312 PROP_CODEC_0,
|
|
1313 PROP_ID,
|
|
1314 PROP_ENCODING_NAME,
|
|
1315 PROP_MEDIA_TYPE,
|
|
1316 PROP_CLOCK_RATE,
|
|
1317 PROP_CHANNELS,
|
|
1318 PROP_OPTIONAL_PARAMS,
|
|
1319 };
|
|
1320
|
|
1321 static void
|
|
1322 purple_media_codec_init(PurpleMediaCodec *info)
|
|
1323 {
|
|
1324 PurpleMediaCodecPrivate *priv =
|
|
1325 PURPLE_MEDIA_CODEC_GET_PRIVATE(info);
|
|
1326 priv->encoding_name = NULL;
|
|
1327 priv->optional_params = NULL;
|
|
1328 }
|
|
1329
|
|
1330 static void
|
|
1331 purple_media_codec_finalize(GObject *info)
|
|
1332 {
|
|
1333 PurpleMediaCodecPrivate *priv =
|
|
1334 PURPLE_MEDIA_CODEC_GET_PRIVATE(info);
|
|
1335 g_free(priv->encoding_name);
|
|
1336 for (; priv->optional_params; priv->optional_params =
|
|
1337 g_list_delete_link(priv->optional_params,
|
|
1338 priv->optional_params)) {
|
|
1339 g_free(priv->optional_params->data);
|
|
1340 }
|
|
1341 }
|
|
1342
|
|
1343 static void
|
|
1344 purple_media_codec_set_property (GObject *object, guint prop_id,
|
|
1345 const GValue *value, GParamSpec *pspec)
|
|
1346 {
|
|
1347 PurpleMediaCodecPrivate *priv;
|
|
1348 g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object));
|
|
1349
|
|
1350 priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object);
|
|
1351
|
|
1352 switch (prop_id) {
|
|
1353 case PROP_ID:
|
|
1354 priv->id = g_value_get_uint(value);
|
|
1355 break;
|
|
1356 case PROP_ENCODING_NAME:
|
|
1357 g_free(priv->encoding_name);
|
|
1358 priv->encoding_name = g_value_dup_string(value);
|
|
1359 break;
|
|
1360 case PROP_MEDIA_TYPE:
|
|
1361 priv->media_type = g_value_get_flags(value);
|
|
1362 break;
|
|
1363 case PROP_CLOCK_RATE:
|
|
1364 priv->clock_rate = g_value_get_uint(value);
|
|
1365 break;
|
|
1366 case PROP_CHANNELS:
|
|
1367 priv->channels = g_value_get_uint(value);
|
|
1368 break;
|
|
1369 case PROP_OPTIONAL_PARAMS:
|
|
1370 priv->optional_params = g_value_get_pointer(value);
|
|
1371 break;
|
|
1372 default:
|
|
1373 G_OBJECT_WARN_INVALID_PROPERTY_ID(
|
|
1374 object, prop_id, pspec);
|
|
1375 break;
|
|
1376 }
|
|
1377 }
|
|
1378
|
|
1379 static void
|
|
1380 purple_media_codec_get_property (GObject *object, guint prop_id,
|
|
1381 GValue *value, GParamSpec *pspec)
|
|
1382 {
|
|
1383 PurpleMediaCodecPrivate *priv;
|
|
1384 g_return_if_fail(PURPLE_IS_MEDIA_CODEC(object));
|
|
1385
|
|
1386 priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(object);
|
|
1387
|
|
1388 switch (prop_id) {
|
|
1389 case PROP_ID:
|
|
1390 g_value_set_uint(value, priv->id);
|
|
1391 break;
|
|
1392 case PROP_ENCODING_NAME:
|
|
1393 g_value_set_string(value, priv->encoding_name);
|
|
1394 break;
|
|
1395 case PROP_MEDIA_TYPE:
|
|
1396 g_value_set_flags(value, priv->media_type);
|
|
1397 break;
|
|
1398 case PROP_CLOCK_RATE:
|
|
1399 g_value_set_uint(value, priv->clock_rate);
|
|
1400 break;
|
|
1401 case PROP_CHANNELS:
|
|
1402 g_value_set_uint(value, priv->channels);
|
|
1403 break;
|
|
1404 case PROP_OPTIONAL_PARAMS:
|
|
1405 g_value_set_pointer(value, priv->optional_params);
|
|
1406 break;
|
|
1407 default:
|
|
1408 G_OBJECT_WARN_INVALID_PROPERTY_ID(
|
|
1409 object, prop_id, pspec);
|
|
1410 break;
|
|
1411 }
|
|
1412 }
|
|
1413
|
|
1414 static void
|
|
1415 purple_media_codec_class_init(PurpleMediaCodecClass *klass)
|
|
1416 {
|
|
1417 GObjectClass *gobject_class = (GObjectClass*)klass;
|
|
1418
|
|
1419 gobject_class->finalize = purple_media_codec_finalize;
|
|
1420 gobject_class->set_property = purple_media_codec_set_property;
|
|
1421 gobject_class->get_property = purple_media_codec_get_property;
|
|
1422
|
|
1423 g_object_class_install_property(gobject_class, PROP_ID,
|
|
1424 g_param_spec_uint("id",
|
|
1425 "ID",
|
|
1426 "The numeric identifier of the codec.",
|
|
1427 0, G_MAXUINT, 0,
|
|
1428 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
|
|
1429
|
|
1430 g_object_class_install_property(gobject_class, PROP_ENCODING_NAME,
|
|
1431 g_param_spec_string("encoding-name",
|
|
1432 "Encoding Name",
|
|
1433 "The name of the codec.",
|
|
1434 NULL,
|
|
1435 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
|
|
1436
|
|
1437 g_object_class_install_property(gobject_class, PROP_MEDIA_TYPE,
|
|
1438 g_param_spec_flags("media-type",
|
|
1439 "Media Type",
|
|
1440 "Whether this is an audio of video codec.",
|
|
1441 PURPLE_TYPE_MEDIA_SESSION_TYPE,
|
|
1442 PURPLE_MEDIA_NONE,
|
|
1443 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
|
|
1444
|
|
1445 g_object_class_install_property(gobject_class, PROP_CLOCK_RATE,
|
|
1446 g_param_spec_uint("clock-rate",
|
|
1447 "Create Callback",
|
|
1448 "The function called to create this element.",
|
|
1449 0, G_MAXUINT, 0,
|
|
1450 G_PARAM_READWRITE));
|
|
1451
|
|
1452 g_object_class_install_property(gobject_class, PROP_CHANNELS,
|
|
1453 g_param_spec_uint("channels",
|
|
1454 "Channels",
|
|
1455 "The number of channels in this codec.",
|
|
1456 0, G_MAXUINT, 0,
|
|
1457 G_PARAM_READWRITE));
|
|
1458 g_object_class_install_property(gobject_class, PROP_OPTIONAL_PARAMS,
|
|
1459 g_param_spec_pointer("optional-params",
|
|
1460 "Optional Params",
|
|
1461 "A list of optional parameters for the codec.",
|
|
1462 G_PARAM_READWRITE));
|
|
1463
|
|
1464 g_type_class_add_private(klass, sizeof(PurpleMediaCodecPrivate));
|
|
1465 }
|
|
1466
|
|
1467 G_DEFINE_TYPE(PurpleMediaCodec,
|
|
1468 purple_media_codec, G_TYPE_OBJECT);
|
|
1469 #else
|
|
1470 GType
|
|
1471 purple_media_codec_get_type()
|
|
1472 {
|
|
1473 return G_TYPE_NONE;
|
|
1474 }
|
|
1475 #endif
|
|
1476
|
|
1477 guint
|
|
1478 purple_media_codec_get_id(PurpleMediaCodec *codec)
|
|
1479 {
|
|
1480 guint id;
|
|
1481 g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0);
|
|
1482 g_object_get(codec, "id", &id, NULL);
|
|
1483 return id;
|
|
1484 }
|
|
1485
|
|
1486 gchar *
|
|
1487 purple_media_codec_get_encoding_name(PurpleMediaCodec *codec)
|
|
1488 {
|
|
1489 gchar *name;
|
|
1490 g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL);
|
|
1491 g_object_get(codec, "encoding-name", &name, NULL);
|
|
1492 return name;
|
|
1493 }
|
|
1494
|
|
1495 guint
|
|
1496 purple_media_codec_get_clock_rate(PurpleMediaCodec *codec)
|
|
1497 {
|
|
1498 guint clock_rate;
|
|
1499 g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0);
|
|
1500 g_object_get(codec, "clock-rate", &clock_rate, NULL);
|
|
1501 return clock_rate;
|
|
1502 }
|
|
1503
|
|
1504 guint
|
|
1505 purple_media_codec_get_channels(PurpleMediaCodec *codec)
|
|
1506 {
|
|
1507 guint channels;
|
|
1508 g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), 0);
|
|
1509 g_object_get(codec, "channels", &channels, NULL);
|
|
1510 return channels;
|
|
1511 }
|
|
1512
|
|
1513 GList *
|
|
1514 purple_media_codec_get_optional_parameters(PurpleMediaCodec *codec)
|
|
1515 {
|
|
1516 GList *optional_params;
|
|
1517 g_return_val_if_fail(PURPLE_IS_MEDIA_CODEC(codec), NULL);
|
|
1518 g_object_get(codec, "optional-params", &optional_params, NULL);
|
|
1519 return optional_params;
|
|
1520 }
|
|
1521
|
|
1522 void
|
|
1523 purple_media_codec_add_optional_parameter(PurpleMediaCodec *codec,
|
|
1524 const gchar *name, const gchar *value)
|
|
1525 {
|
|
1526 #ifdef USE_VV
|
|
1527 PurpleMediaCodecPrivate *priv;
|
|
1528 PurpleKeyValuePair *new_param;
|
|
1529
|
|
1530 g_return_if_fail(codec != NULL);
|
|
1531 g_return_if_fail(name != NULL && value != NULL);
|
|
1532
|
|
1533 priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
|
|
1534
|
|
1535 new_param = g_new0(PurpleKeyValuePair, 1);
|
|
1536 new_param->key = g_strdup(name);
|
|
1537 new_param->value = g_strdup(value);
|
|
1538 priv->optional_params = g_list_append(
|
|
1539 priv->optional_params, new_param);
|
|
1540 #endif
|
|
1541 }
|
|
1542
|
|
1543 void
|
|
1544 purple_media_codec_remove_optional_parameter(PurpleMediaCodec *codec,
|
|
1545 PurpleKeyValuePair *param)
|
|
1546 {
|
|
1547 #ifdef USE_VV
|
|
1548 PurpleMediaCodecPrivate *priv;
|
|
1549
|
|
1550 g_return_if_fail(codec != NULL && param != NULL);
|
|
1551
|
|
1552 priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
|
|
1553
|
|
1554 g_free(param->key);
|
|
1555 g_free(param->value);
|
|
1556 g_free(param);
|
|
1557
|
|
1558 priv->optional_params =
|
|
1559 g_list_remove(priv->optional_params, param);
|
|
1560 #endif
|
|
1561 }
|
|
1562
|
|
1563 PurpleKeyValuePair *
|
|
1564 purple_media_codec_get_optional_parameter(PurpleMediaCodec *codec,
|
|
1565 const gchar *name, const gchar *value)
|
|
1566 {
|
|
1567 #ifdef USE_VV
|
|
1568 PurpleMediaCodecPrivate *priv;
|
|
1569 GList *iter;
|
|
1570
|
|
1571 g_return_val_if_fail(codec != NULL, NULL);
|
|
1572 g_return_val_if_fail(name != NULL, NULL);
|
|
1573
|
|
1574 priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
|
|
1575
|
|
1576 for (iter = priv->optional_params; iter; iter = g_list_next(iter)) {
|
|
1577 PurpleKeyValuePair *param = iter->data;
|
|
1578 if (!g_ascii_strcasecmp(param->key, name) &&
|
|
1579 (value == NULL ||
|
|
1580 !g_ascii_strcasecmp(param->value, value)))
|
|
1581 return param;
|
|
1582 }
|
|
1583 #endif
|
|
1584
|
|
1585 return NULL;
|
|
1586 }
|
|
1587
|
|
1588 PurpleMediaCodec *
|
|
1589 purple_media_codec_new(int id, const char *encoding_name,
|
|
1590 PurpleMediaSessionType media_type, guint clock_rate)
|
|
1591 {
|
|
1592 PurpleMediaCodec *codec =
|
|
1593 g_object_new(PURPLE_TYPE_MEDIA_CODEC,
|
|
1594 "id", id,
|
|
1595 "encoding_name", encoding_name,
|
|
1596 "media_type", media_type,
|
|
1597 "clock-rate", clock_rate, NULL);
|
|
1598 return codec;
|
|
1599 }
|
|
1600
|
|
1601 static PurpleMediaCodec *
|
|
1602 purple_media_codec_copy(PurpleMediaCodec *codec)
|
|
1603 {
|
|
1604 #ifdef USE_VV
|
|
1605 PurpleMediaCodecPrivate *priv;
|
|
1606 PurpleMediaCodec *new_codec;
|
|
1607 GList *iter;
|
|
1608
|
|
1609 if (codec == NULL)
|
|
1610 return NULL;
|
|
1611
|
|
1612 priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
|
|
1613
|
|
1614 new_codec = purple_media_codec_new(priv->id, priv->encoding_name,
|
|
1615 priv->media_type, priv->clock_rate);
|
|
1616 g_object_set(codec, "channels", priv->channels, NULL);
|
|
1617
|
|
1618 for (iter = priv->optional_params; iter; iter = g_list_next(iter)) {
|
|
1619 PurpleKeyValuePair *param =
|
|
1620 (PurpleKeyValuePair*)iter->data;
|
|
1621 purple_media_codec_add_optional_parameter(new_codec,
|
|
1622 param->key, param->value);
|
|
1623 }
|
|
1624
|
|
1625 return new_codec;
|
|
1626 #else
|
|
1627 return NULL;
|
|
1628 #endif
|
|
1629 }
|
|
1630
|
|
1631 #ifdef USE_VV
|
|
1632 static FsCodec *
|
|
1633 purple_media_codec_to_fs(const PurpleMediaCodec *codec)
|
|
1634 {
|
|
1635 PurpleMediaCodecPrivate *priv;
|
|
1636 FsCodec *new_codec;
|
|
1637 GList *iter;
|
|
1638
|
|
1639 if (codec == NULL)
|
|
1640 return NULL;
|
|
1641
|
|
1642 priv = PURPLE_MEDIA_CODEC_GET_PRIVATE(codec);
|
|
1643
|
|
1644 new_codec = fs_codec_new(priv->id, priv->encoding_name,
|
|
1645 purple_media_to_fs_media_type(priv->media_type),
|
|
1646 priv->clock_rate);
|
|
1647 new_codec->channels = priv->channels;
|
|
1648
|
|
1649 for (iter = priv->optional_params; iter; iter = g_list_next(iter)) {
|
|
1650 PurpleKeyValuePair *param = (PurpleKeyValuePair*)iter->data;
|
|
1651 fs_codec_add_optional_parameter(new_codec,
|
|
1652 param->key, param->value);
|
|
1653 }
|
|
1654
|
|
1655 return new_codec;
|
|
1656 }
|
|
1657
|
|
1658 static PurpleMediaCodec *
|
|
1659 purple_media_codec_from_fs(const FsCodec *codec)
|
|
1660 {
|
|
1661 PurpleMediaCodec *new_codec;
|
|
1662 GList *iter;
|
|
1663
|
|
1664 if (codec == NULL)
|
|
1665 return NULL;
|
|
1666
|
|
1667 new_codec = purple_media_codec_new(codec->id, codec->encoding_name,
|
|
1668 purple_media_from_fs(codec->media_type,
|
|
1669 FS_DIRECTION_BOTH), codec->clock_rate);
|
|
1670 g_object_set(new_codec, "channels", codec->channels, NULL);
|
|
1671
|
|
1672 for (iter = codec->optional_params; iter; iter = g_list_next(iter)) {
|
|
1673 FsCodecParameter *param = (FsCodecParameter*)iter->data;
|
|
1674 purple_media_codec_add_optional_parameter(new_codec,
|
|
1675 param->name, param->value);
|
|
1676 }
|
|
1677
|
|
1678 return new_codec;
|
|
1679 }
|
|
1680 #endif
|
|
1681
|
|
1682 gchar *
|
|
1683 purple_media_codec_to_string(const PurpleMediaCodec *codec)
|
|
1684 {
|
|
1685 #ifdef USE_VV
|
|
1686 FsCodec *fscodec = purple_media_codec_to_fs(codec);
|
|
1687 gchar *str = fs_codec_to_string(fscodec);
|
|
1688 fs_codec_destroy(fscodec);
|
|
1689 return str;
|
|
1690 #else
|
|
1691 return g_strdup("");
|
|
1692 #endif
|
|
1693 }
|
|
1694
|
|
1695 #ifdef USE_VV
|
|
1696 static GList *
|
|
1697 purple_media_codec_list_from_fs(GList *codecs)
|
|
1698 {
|
|
1699 GList *new_list = NULL;
|
|
1700
|
|
1701 for (; codecs; codecs = g_list_next(codecs)) {
|
|
1702 new_list = g_list_prepend(new_list,
|
|
1703 purple_media_codec_from_fs(
|
|
1704 codecs->data));
|
|
1705 }
|
|
1706
|
|
1707 new_list = g_list_reverse(new_list);
|
|
1708 return new_list;
|
|
1709 }
|
|
1710
|
|
1711 static GList *
|
|
1712 purple_media_codec_list_to_fs(GList *codecs)
|
|
1713 {
|
|
1714 GList *new_list = NULL;
|
|
1715
|
|
1716 for (; codecs; codecs = g_list_next(codecs)) {
|
|
1717 new_list = g_list_prepend(new_list,
|
|
1718 purple_media_codec_to_fs(
|
|
1719 codecs->data));
|
|
1720 }
|
|
1721
|
|
1722 new_list = g_list_reverse(new_list);
|
|
1723 return new_list;
|
|
1724 }
|
|
1725 #endif
|
|
1726
|
|
1727 GList *
|
|
1728 purple_media_codec_list_copy(GList *codecs)
|
|
1729 {
|
|
1730 GList *new_list = NULL;
|
|
1731
|
|
1732 for (; codecs; codecs = g_list_next(codecs)) {
|
|
1733 new_list = g_list_prepend(new_list,
|
|
1734 purple_media_codec_copy(codecs->data));
|
|
1735 }
|
|
1736
|
|
1737 new_list = g_list_reverse(new_list);
|
|
1738 return new_list;
|
|
1739 }
|
|
1740
|
|
1741 void
|
|
1742 purple_media_codec_list_free(GList *codecs)
|
|
1743 {
|
|
1744 for (; codecs; codecs =
|
|
1745 g_list_delete_link(codecs, codecs)) {
|
|
1746 g_object_unref(codecs->data);
|
|
1747 }
|
|
1748 }
|
|
1749
|
|
1750 #ifdef USE_VV
|
|
1751 static PurpleMediaSession*
|
|
1752 purple_media_get_session(PurpleMedia *media, const gchar *sess_id)
|
|
1753 {
|
|
1754 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
1755 return (PurpleMediaSession*) (media->priv->sessions) ?
|
|
1756 g_hash_table_lookup(media->priv->sessions, sess_id) : NULL;
|
|
1757 }
|
|
1758
|
|
1759 static FsParticipant*
|
|
1760 purple_media_get_participant(PurpleMedia *media, const gchar *name)
|
|
1761 {
|
|
1762 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
1763 return (FsParticipant*) (media->priv->participants) ?
|
|
1764 g_hash_table_lookup(media->priv->participants, name) : NULL;
|
|
1765 }
|
|
1766
|
|
1767 static PurpleMediaStream*
|
|
1768 purple_media_get_stream(PurpleMedia *media, const gchar *session, const gchar *participant)
|
|
1769 {
|
|
1770 GList *streams;
|
|
1771
|
|
1772 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
1773
|
|
1774 streams = media->priv->streams;
|
|
1775
|
|
1776 for (; streams; streams = g_list_next(streams)) {
|
|
1777 PurpleMediaStream *stream = streams->data;
|
|
1778 if (!strcmp(stream->session->id, session) &&
|
|
1779 !strcmp(stream->participant, participant))
|
|
1780 return stream;
|
|
1781 }
|
|
1782
|
|
1783 return NULL;
|
|
1784 }
|
|
1785
|
|
1786 static GList *
|
|
1787 purple_media_get_streams(PurpleMedia *media, const gchar *session,
|
|
1788 const gchar *participant)
|
|
1789 {
|
|
1790 GList *streams;
|
|
1791 GList *ret = NULL;
|
|
1792
|
|
1793 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
1794
|
|
1795 streams = media->priv->streams;
|
|
1796
|
|
1797 for (; streams; streams = g_list_next(streams)) {
|
|
1798 PurpleMediaStream *stream = streams->data;
|
|
1799 if ((session == NULL ||
|
|
1800 !strcmp(stream->session->id, session)) &&
|
|
1801 (participant == NULL ||
|
|
1802 !strcmp(stream->participant, participant)))
|
|
1803 ret = g_list_append(ret, stream);
|
|
1804 }
|
|
1805
|
|
1806 return ret;
|
|
1807 }
|
|
1808
|
|
1809 static void
|
|
1810 purple_media_add_session(PurpleMedia *media, PurpleMediaSession *session)
|
|
1811 {
|
|
1812 g_return_if_fail(PURPLE_IS_MEDIA(media));
|
|
1813 g_return_if_fail(session != NULL);
|
|
1814
|
|
1815 if (!media->priv->sessions) {
|
|
1816 purple_debug_info("media", "Creating hash table for sessions\n");
|
|
1817 media->priv->sessions = g_hash_table_new(g_str_hash, g_str_equal);
|
|
1818 }
|
|
1819 g_hash_table_insert(media->priv->sessions, g_strdup(session->id), session);
|
|
1820 }
|
|
1821
|
|
1822 static gboolean
|
|
1823 purple_media_remove_session(PurpleMedia *media, PurpleMediaSession *session)
|
|
1824 {
|
|
1825 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
|
|
1826 return g_hash_table_remove(media->priv->sessions, session->id);
|
|
1827 }
|
|
1828
|
|
1829 static FsParticipant *
|
|
1830 purple_media_add_participant(PurpleMedia *media, const gchar *name)
|
|
1831 {
|
|
1832 FsParticipant *participant;
|
|
1833 GError *err = NULL;
|
|
1834
|
|
1835 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
1836
|
|
1837 participant = purple_media_get_participant(media, name);
|
|
1838
|
|
1839 if (participant)
|
|
1840 return participant;
|
|
1841
|
|
1842 participant = fs_conference_new_participant(media->priv->conference,
|
|
1843 (gchar*)name, &err);
|
|
1844
|
|
1845 if (err) {
|
|
1846 purple_debug_error("media", "Error creating participant: %s\n",
|
|
1847 err->message);
|
|
1848 g_error_free(err);
|
|
1849 return NULL;
|
|
1850 }
|
|
1851
|
|
1852 if (!media->priv->participants) {
|
|
1853 purple_debug_info("media", "Creating hash table for participants\n");
|
|
1854 media->priv->participants = g_hash_table_new_full(g_str_hash,
|
|
1855 g_str_equal, g_free, NULL);
|
|
1856 }
|
|
1857
|
|
1858 g_hash_table_insert(media->priv->participants, g_strdup(name), participant);
|
|
1859
|
|
1860 return participant;
|
|
1861 }
|
|
1862
|
|
1863 static PurpleMediaStream *
|
|
1864 purple_media_insert_stream(PurpleMediaSession *session, const gchar *name, FsStream *stream)
|
|
1865 {
|
|
1866 PurpleMediaStream *media_stream;
|
|
1867
|
|
1868 g_return_val_if_fail(session != NULL, NULL);
|
|
1869
|
|
1870 media_stream = g_new0(PurpleMediaStream, 1);
|
|
1871 media_stream->stream = stream;
|
|
1872 media_stream->participant = g_strdup(name);
|
|
1873 media_stream->session = session;
|
|
1874
|
|
1875 session->media->priv->streams =
|
|
1876 g_list_append(session->media->priv->streams, media_stream);
|
|
1877
|
|
1878 return media_stream;
|
|
1879 }
|
|
1880
|
|
1881 static void
|
|
1882 purple_media_insert_local_candidate(PurpleMediaSession *session, const gchar *name,
|
|
1883 FsCandidate *candidate)
|
|
1884 {
|
|
1885 PurpleMediaStream *stream;
|
|
1886
|
|
1887 g_return_if_fail(session != NULL);
|
|
1888
|
|
1889 stream = purple_media_get_stream(session->media, session->id, name);
|
|
1890 stream->local_candidates = g_list_append(stream->local_candidates, candidate);
|
|
1891 }
|
|
1892 #endif
|
|
1893
|
|
1894 GList *
|
|
1895 purple_media_get_session_ids(PurpleMedia *media)
|
|
1896 {
|
|
1897 #ifdef USE_VV
|
|
1898 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
1899 return media->priv->sessions != NULL ?
|
|
1900 g_hash_table_get_keys(media->priv->sessions) : NULL;
|
|
1901 #else
|
|
1902 return NULL;
|
|
1903 #endif
|
|
1904 }
|
|
1905
|
|
1906 #ifdef USE_VV
|
|
1907 static void
|
|
1908 purple_media_set_src(PurpleMedia *media, const gchar *sess_id, GstElement *src)
|
|
1909 {
|
|
1910 PurpleMediaSession *session;
|
|
1911 GstPad *sinkpad;
|
|
1912 GstPad *srcpad;
|
|
1913
|
|
1914 g_return_if_fail(PURPLE_IS_MEDIA(media));
|
|
1915 g_return_if_fail(GST_IS_ELEMENT(src));
|
|
1916
|
|
1917 session = purple_media_get_session(media, sess_id);
|
|
1918
|
|
1919 if (session == NULL) {
|
|
1920 purple_debug_warning("media", "purple_media_set_src: trying"
|
|
1921 " to set src on non-existent session\n");
|
|
1922 return;
|
|
1923 }
|
|
1924
|
|
1925 if (session->src)
|
|
1926 gst_object_unref(session->src);
|
|
1927 session->src = src;
|
|
1928 gst_element_set_locked_state(session->src, TRUE);
|
|
1929
|
|
1930 session->tee = gst_element_factory_make("tee", NULL);
|
|
1931 gst_bin_add(GST_BIN(session->media->priv->confbin), session->tee);
|
|
1932
|
|
1933 /* This supposedly isn't necessary, but it silences some warnings */
|
|
1934 if (GST_ELEMENT_PARENT(session->media->priv->confbin)
|
|
1935 == GST_ELEMENT_PARENT(session->src)) {
|
|
1936 GstPad *pad = gst_element_get_static_pad(session->tee, "sink");
|
|
1937 GstPad *ghost = gst_ghost_pad_new(NULL, pad);
|
|
1938 gst_object_unref(pad);
|
|
1939 gst_pad_set_active(ghost, TRUE);
|
|
1940 gst_element_add_pad(session->media->priv->confbin, ghost);
|
|
1941 }
|
|
1942
|
|
1943 gst_element_set_state(session->tee, GST_STATE_PLAYING);
|
|
1944 gst_element_link(session->src, session->media->priv->confbin);
|
|
1945
|
|
1946 g_object_get(session->session, "sink-pad", &sinkpad, NULL);
|
|
1947 if (session->type & PURPLE_MEDIA_SEND_AUDIO) {
|
|
1948 gchar *name = g_strdup_printf("volume_%s", session->id);
|
|
1949 GstElement *level;
|
|
1950 GstElement *volume = gst_element_factory_make("volume", name);
|
|
1951 double input_volume = purple_prefs_get_int(
|
|
1952 "/purple/media/audio/volume/input")/10.0;
|
|
1953 g_free(name);
|
|
1954 name = g_strdup_printf("sendlevel_%s", session->id);
|
|
1955 level = gst_element_factory_make("level", name);
|
|
1956 g_free(name);
|
|
1957 gst_bin_add(GST_BIN(session->media->priv->confbin), volume);
|
|
1958 gst_bin_add(GST_BIN(session->media->priv->confbin), level);
|
|
1959 gst_element_set_state(level, GST_STATE_PLAYING);
|
|
1960 gst_element_set_state(volume, GST_STATE_PLAYING);
|
|
1961 gst_element_link(volume, level);
|
|
1962 gst_element_link(session->tee, volume);
|
|
1963 srcpad = gst_element_get_static_pad(level, "src");
|
|
1964 g_object_set(volume, "volume", input_volume, NULL);
|
|
1965 } else {
|
|
1966 srcpad = gst_element_get_request_pad(session->tee, "src%d");
|
|
1967 }
|
|
1968 purple_debug_info("media", "connecting pad: %s\n",
|
|
1969 gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK
|
|
1970 ? "success" : "failure");
|
|
1971 gst_element_set_locked_state(session->src, FALSE);
|
|
1972 gst_object_unref(session->src);
|
|
1973 }
|
|
1974 #endif
|
|
1975
|
|
1976 #ifdef USE_GSTREAMER
|
|
1977 GstElement *
|
|
1978 purple_media_get_src(PurpleMedia *media, const gchar *sess_id)
|
|
1979 {
|
|
1980 #ifdef USE_VV
|
|
1981 PurpleMediaSession *session;
|
|
1982 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
1983 session = purple_media_get_session(media, sess_id);
|
|
1984 return (session != NULL) ? session->src : NULL;
|
|
1985 #else
|
|
1986 return NULL;
|
|
1987 #endif
|
|
1988 }
|
|
1989 #endif /* USE_GSTREAMER */
|
|
1990
|
|
1991 #ifdef USE_VV
|
|
1992 static PurpleMediaSession *
|
|
1993 purple_media_session_from_fs_stream(PurpleMedia *media, FsStream *stream)
|
|
1994 {
|
|
1995 FsSession *fssession;
|
|
1996 GList *values;
|
|
1997
|
|
1998 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
1999 g_return_val_if_fail(FS_IS_STREAM(stream), NULL);
|
|
2000
|
|
2001 g_object_get(stream, "session", &fssession, NULL);
|
|
2002
|
|
2003 values = g_hash_table_get_values(media->priv->sessions);
|
|
2004
|
|
2005 for (; values; values = g_list_delete_link(values, values)) {
|
|
2006 PurpleMediaSession *session = values->data;
|
|
2007
|
|
2008 if (session->session == fssession) {
|
|
2009 g_list_free(values);
|
|
2010 g_object_unref(fssession);
|
|
2011 return session;
|
|
2012 }
|
|
2013 }
|
|
2014
|
|
2015 g_object_unref(fssession);
|
|
2016 return NULL;
|
|
2017 }
|
|
2018
|
|
2019 static gboolean
|
|
2020 media_bus_call(GstBus *bus, GstMessage *msg, PurpleMedia *media)
|
|
2021 {
|
|
2022 switch(GST_MESSAGE_TYPE(msg)) {
|
|
2023 case GST_MESSAGE_ELEMENT: {
|
|
2024 if (g_signal_has_handler_pending(media,
|
|
2025 purple_media_signals[LEVEL], 0, FALSE)
|
|
2026 && gst_structure_has_name(
|
|
2027 gst_message_get_structure(msg), "level")) {
|
|
2028 GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
|
|
2029 gchar *name;
|
|
2030 gchar *participant = NULL;
|
|
2031 PurpleMediaSession *session = NULL;
|
|
2032 gdouble rms_db;
|
|
2033 gdouble percent;
|
|
2034 const GValue *list;
|
|
2035 const GValue *value;
|
|
2036
|
|
2037 if (!PURPLE_IS_MEDIA(media) ||
|
|
2038 GST_ELEMENT_PARENT(src) !=
|
|
2039 media->priv->confbin)
|
|
2040 break;
|
|
2041
|
|
2042 name = gst_element_get_name(src);
|
|
2043 if (!strncmp(name, "sendlevel_", 10)) {
|
|
2044 session = purple_media_get_session(
|
|
2045 media, name+10);
|
|
2046 } else {
|
|
2047 GList *iter = media->priv->streams;
|
|
2048 for (; iter; iter = g_list_next(iter)) {
|
|
2049 PurpleMediaStream *stream = iter->data;
|
|
2050 if (stream->level == src) {
|
|
2051 session = stream->session;
|
|
2052 participant = stream->participant;
|
|
2053 break;
|
|
2054 }
|
|
2055 }
|
|
2056 }
|
|
2057 g_free(name);
|
|
2058 if (!session)
|
|
2059 break;
|
|
2060
|
|
2061 list = gst_structure_get_value(
|
|
2062 gst_message_get_structure(msg), "rms");
|
|
2063 value = gst_value_list_get_value(list, 0);
|
|
2064 rms_db = g_value_get_double(value);
|
|
2065 percent = pow(10, rms_db / 20) * 5;
|
|
2066 if(percent > 1.0)
|
|
2067 percent = 1.0;
|
|
2068
|
|
2069 g_signal_emit(media, purple_media_signals[LEVEL],
|
|
2070 0, session->id, participant, percent);
|
|
2071 break;
|
|
2072 }
|
|
2073 if (!FS_IS_CONFERENCE(GST_MESSAGE_SRC(msg)) ||
|
|
2074 !PURPLE_IS_MEDIA(media) ||
|
|
2075 media->priv->conference !=
|
|
2076 FS_CONFERENCE(GST_MESSAGE_SRC(msg)))
|
|
2077 break;
|
|
2078
|
|
2079 if (gst_structure_has_name(msg->structure, "farsight-error")) {
|
|
2080 FsError error_no;
|
|
2081 gst_structure_get_enum(msg->structure, "error-no",
|
|
2082 FS_TYPE_ERROR, (gint*)&error_no);
|
|
2083 switch (error_no) {
|
|
2084 case FS_ERROR_NO_CODECS:
|
|
2085 purple_media_error(media, _("No codecs found. Install some GStreamer codecs found in GStreamer plugins packages."));
|
|
2086 purple_media_end(media, NULL, NULL);
|
|
2087 break;
|
|
2088 case FS_ERROR_NO_CODECS_LEFT:
|
|
2089 purple_media_error(media, _("No codecs left. Your codec preferences in fs-codecs.conf are too strict."));
|
|
2090 purple_media_end(media, NULL, NULL);
|
|
2091 break;
|
|
2092 case FS_ERROR_UNKNOWN_CNAME:
|
|
2093 /*
|
|
2094 * Unknown CName is only a problem for the
|
|
2095 * multicast transmitter which isn't used.
|
|
2096 * It is also deprecated.
|
|
2097 */
|
|
2098 break;
|
|
2099 default:
|
|
2100 purple_debug_error("media", "farsight-error: %i: %s\n", error_no,
|
|
2101 gst_structure_get_string(msg->structure, "error-msg"));
|
|
2102 break;
|
|
2103 }
|
|
2104
|
|
2105 if (FS_ERROR_IS_FATAL(error_no)) {
|
|
2106 purple_media_error(media, _("A non-recoverable Farsight2 error has occurred."));
|
|
2107 purple_media_end(media, NULL, NULL);
|
|
2108 }
|
|
2109 } else if (gst_structure_has_name(msg->structure,
|
|
2110 "farsight-new-local-candidate")) {
|
|
2111 FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream"));
|
|
2112 FsCandidate *local_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "candidate"));
|
|
2113 PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream);
|
|
2114 purple_media_new_local_candidate_cb(stream, local_candidate, session);
|
|
2115 } else if (gst_structure_has_name(msg->structure,
|
|
2116 "farsight-local-candidates-prepared")) {
|
|
2117 FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream"));
|
|
2118 PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream);
|
|
2119 purple_media_candidates_prepared_cb(stream, session);
|
|
2120 } else if (gst_structure_has_name(msg->structure,
|
|
2121 "farsight-new-active-candidate-pair")) {
|
|
2122 FsStream *stream = g_value_get_object(gst_structure_get_value(msg->structure, "stream"));
|
|
2123 FsCandidate *local_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "local-candidate"));
|
|
2124 FsCandidate *remote_candidate = g_value_get_boxed(gst_structure_get_value(msg->structure, "remote-candidate"));
|
|
2125 PurpleMediaSession *session = purple_media_session_from_fs_stream(media, stream);
|
|
2126 purple_media_candidate_pair_established_cb(stream, local_candidate, remote_candidate, session);
|
|
2127 } else if (gst_structure_has_name(msg->structure,
|
|
2128 "farsight-recv-codecs-changed")) {
|
|
2129 GList *codecs = g_value_get_boxed(gst_structure_get_value(msg->structure, "codecs"));
|
|
2130 FsCodec *codec = codecs->data;
|
|
2131 purple_debug_info("media", "farsight-recv-codecs-changed: %s\n", codec->encoding_name);
|
|
2132
|
|
2133 } else if (gst_structure_has_name(msg->structure,
|
|
2134 "farsight-component-state-changed")) {
|
|
2135 FsStreamState fsstate = g_value_get_enum(gst_structure_get_value(msg->structure, "state"));
|
|
2136 guint component = g_value_get_uint(gst_structure_get_value(msg->structure, "component"));
|
|
2137 const gchar *state;
|
|
2138 switch (fsstate) {
|
|
2139 case FS_STREAM_STATE_FAILED:
|
|
2140 state = "FAILED";
|
|
2141 break;
|
|
2142 case FS_STREAM_STATE_DISCONNECTED:
|
|
2143 state = "DISCONNECTED";
|
|
2144 break;
|
|
2145 case FS_STREAM_STATE_GATHERING:
|
|
2146 state = "GATHERING";
|
|
2147 break;
|
|
2148 case FS_STREAM_STATE_CONNECTING:
|
|
2149 state = "CONNECTING";
|
|
2150 break;
|
|
2151 case FS_STREAM_STATE_CONNECTED:
|
|
2152 state = "CONNECTED";
|
|
2153 break;
|
|
2154 case FS_STREAM_STATE_READY:
|
|
2155 state = "READY";
|
|
2156 break;
|
|
2157 default:
|
|
2158 state = "UNKNOWN";
|
|
2159 break;
|
|
2160 }
|
|
2161 purple_debug_info("media", "farsight-component-state-changed: component: %u state: %s\n", component, state);
|
|
2162 } else if (gst_structure_has_name(msg->structure,
|
|
2163 "farsight-send-codec-changed")) {
|
|
2164 FsCodec *codec = g_value_get_boxed(gst_structure_get_value(msg->structure, "codec"));
|
|
2165 gchar *codec_str = fs_codec_to_string(codec);
|
|
2166 purple_debug_info("media", "farsight-send-codec-changed: codec: %s\n", codec_str);
|
|
2167 g_free(codec_str);
|
|
2168 } else if (gst_structure_has_name(msg->structure,
|
|
2169 "farsight-codecs-changed")) {
|
|
2170 GList *sessions = g_hash_table_get_values(PURPLE_MEDIA(media)->priv->sessions);
|
|
2171 FsSession *fssession = g_value_get_object(gst_structure_get_value(msg->structure, "session"));
|
|
2172 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
|
|
2173 PurpleMediaSession *session = sessions->data;
|
|
2174 if (session->session == fssession) {
|
|
2175 gchar *session_id = g_strdup(session->id);
|
|
2176 g_signal_emit(media, purple_media_signals[CODECS_CHANGED], 0, session_id);
|
|
2177 g_free(session_id);
|
|
2178 g_list_free(sessions);
|
|
2179 break;
|
|
2180 }
|
|
2181 }
|
|
2182 }
|
|
2183 break;
|
|
2184 }
|
|
2185 case GST_MESSAGE_ERROR: {
|
|
2186 GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(msg));
|
|
2187 GstElement *lastElement = NULL;
|
|
2188 while (!GST_IS_PIPELINE(element)) {
|
|
2189 if (element == media->priv->confbin) {
|
|
2190 purple_media_error(media, _("Conference error"));
|
|
2191 purple_media_end(media, NULL, NULL);
|
|
2192 break;
|
|
2193 }
|
|
2194 lastElement = element;
|
|
2195 element = GST_ELEMENT_PARENT(element);
|
|
2196 }
|
|
2197 if (GST_IS_PIPELINE(element)) {
|
|
2198 GList *sessions = g_hash_table_get_values(media->priv->sessions);
|
|
2199 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
|
|
2200 PurpleMediaSession *session = sessions->data;
|
|
2201
|
|
2202 if (session->src == lastElement) {
|
|
2203 if (session->type & PURPLE_MEDIA_AUDIO)
|
|
2204 purple_media_error(media, _("Error with your microphone"));
|
|
2205 else
|
|
2206 purple_media_error(media, _("Error with your webcam"));
|
|
2207 purple_media_end(media, NULL, NULL);
|
|
2208 break;
|
|
2209 }
|
|
2210 }
|
|
2211 g_list_free(sessions);
|
|
2212 }
|
|
2213 }
|
|
2214 default:
|
|
2215 break;
|
|
2216 }
|
|
2217
|
|
2218 return TRUE;
|
|
2219 }
|
|
2220 #endif
|
|
2221
|
|
2222 PurpleAccount *
|
|
2223 purple_media_get_account(PurpleMedia *media)
|
|
2224 {
|
|
2225 #ifdef USE_VV
|
|
2226 PurpleAccount *account;
|
|
2227 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
2228 g_object_get(G_OBJECT(media), "account", &account, NULL);
|
|
2229 return account;
|
|
2230 #else
|
|
2231 return NULL;
|
|
2232 #endif
|
|
2233 }
|
|
2234
|
|
2235 gpointer
|
|
2236 purple_media_get_prpl_data(PurpleMedia *media)
|
|
2237 {
|
|
2238 #ifdef USE_VV
|
|
2239 gpointer prpl_data;
|
|
2240 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
2241 g_object_get(G_OBJECT(media), "prpl-data", &prpl_data, NULL);
|
|
2242 return prpl_data;
|
|
2243 #else
|
|
2244 return NULL;
|
|
2245 #endif
|
|
2246 }
|
|
2247
|
|
2248 void
|
|
2249 purple_media_set_prpl_data(PurpleMedia *media, gpointer prpl_data)
|
|
2250 {
|
|
2251 #ifdef USE_VV
|
|
2252 g_return_if_fail(PURPLE_IS_MEDIA(media));
|
|
2253 g_object_set(G_OBJECT(media), "prpl-data", prpl_data, NULL);
|
|
2254 #endif
|
|
2255 }
|
|
2256
|
|
2257 void
|
|
2258 purple_media_error(PurpleMedia *media, const gchar *error, ...)
|
|
2259 {
|
|
2260 #ifdef USE_VV
|
|
2261 va_list args;
|
|
2262 gchar *message;
|
|
2263
|
|
2264 g_return_if_fail(PURPLE_IS_MEDIA(media));
|
|
2265
|
|
2266 va_start(args, error);
|
|
2267 message = g_strdup_vprintf(error, args);
|
|
2268 va_end(args);
|
|
2269
|
|
2270 purple_debug_error("media", "%s\n", message);
|
|
2271 g_signal_emit(media, purple_media_signals[S_ERROR], 0, message);
|
|
2272
|
|
2273 g_free(message);
|
|
2274 #endif
|
|
2275 }
|
|
2276
|
|
2277 void
|
|
2278 purple_media_end(PurpleMedia *media,
|
|
2279 const gchar *session_id, const gchar *participant)
|
|
2280 {
|
|
2281 #ifdef USE_VV
|
|
2282 g_return_if_fail(PURPLE_IS_MEDIA(media));
|
|
2283 if (session_id == NULL && participant == NULL) {
|
|
2284 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
|
|
2285 0, PURPLE_MEDIA_STATE_END,
|
|
2286 NULL, NULL);
|
|
2287 g_object_unref(media);
|
|
2288 }
|
|
2289 #endif
|
|
2290 }
|
|
2291
|
|
2292 void
|
|
2293 purple_media_stream_info(PurpleMedia *media, PurpleMediaInfoType type,
|
|
2294 const gchar *session_id, const gchar *participant,
|
|
2295 gboolean local)
|
|
2296 {
|
|
2297 #ifdef USE_VV
|
|
2298 g_return_if_fail(PURPLE_IS_MEDIA(media));
|
|
2299
|
|
2300 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
|
|
2301 GList *streams;
|
|
2302
|
|
2303 g_return_if_fail(PURPLE_IS_MEDIA(media));
|
|
2304
|
|
2305 streams = purple_media_get_streams(media,
|
|
2306 session_id, participant);
|
|
2307
|
|
2308 for (; streams; streams =
|
|
2309 g_list_delete_link(streams, streams)) {
|
|
2310 PurpleMediaStream *stream = streams->data;
|
|
2311 g_object_set(G_OBJECT(stream->stream), "direction",
|
|
2312 purple_media_to_fs_stream_direction(
|
|
2313 stream->session->type), NULL);
|
|
2314 stream->accepted = TRUE;
|
|
2315
|
|
2316 if (stream->remote_candidates != NULL) {
|
|
2317 GError *err = NULL;
|
|
2318 fs_stream_set_remote_candidates(stream->stream,
|
|
2319 stream->remote_candidates, &err);
|
|
2320
|
|
2321 if (err) {
|
|
2322 purple_debug_error("media", "Error adding remote"
|
|
2323 " candidates: %s\n", err->message);
|
|
2324 g_error_free(err);
|
|
2325 }
|
|
2326 }
|
|
2327 }
|
|
2328 } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_MUTE ||
|
|
2329 type == PURPLE_MEDIA_INFO_UNMUTE)) {
|
|
2330 GList *sessions;
|
|
2331 gboolean active = (type == PURPLE_MEDIA_INFO_MUTE);
|
|
2332
|
|
2333 g_return_if_fail(PURPLE_IS_MEDIA(media));
|
|
2334
|
|
2335 if (session_id == NULL)
|
|
2336 sessions = g_hash_table_get_values(
|
|
2337 media->priv->sessions);
|
|
2338 else
|
|
2339 sessions = g_list_prepend(NULL,
|
|
2340 purple_media_get_session(
|
|
2341 media, session_id));
|
|
2342
|
|
2343 purple_debug_info("media", "Turning mute %s\n",
|
|
2344 active ? "on" : "off");
|
|
2345
|
|
2346 for (; sessions; sessions = g_list_delete_link(
|
|
2347 sessions, sessions)) {
|
|
2348 PurpleMediaSession *session = sessions->data;
|
|
2349 if (session->type & PURPLE_MEDIA_SEND_AUDIO) {
|
|
2350 gchar *name = g_strdup_printf("volume_%s",
|
|
2351 session->id);
|
|
2352 GstElement *volume = gst_bin_get_by_name(
|
|
2353 GST_BIN(session->media->
|
|
2354 priv->confbin), name);
|
|
2355 g_free(name);
|
|
2356 g_object_set(volume, "mute", active, NULL);
|
|
2357 }
|
|
2358 }
|
|
2359 } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_PAUSE ||
|
|
2360 type == PURPLE_MEDIA_INFO_UNPAUSE)) {
|
|
2361 gboolean active = (type == PURPLE_MEDIA_INFO_PAUSE);
|
|
2362 GList *streams = purple_media_get_streams(media,
|
|
2363 session_id, participant);
|
|
2364 for (; streams; streams = g_list_delete_link(streams, streams)) {
|
|
2365 PurpleMediaStream *stream = streams->data;
|
|
2366 if (stream->session->type & PURPLE_MEDIA_SEND_VIDEO) {
|
|
2367 g_object_set(stream->stream, "direction",
|
|
2368 purple_media_to_fs_stream_direction(
|
|
2369 stream->session->type & ((active) ?
|
|
2370 ~PURPLE_MEDIA_SEND_VIDEO :
|
|
2371 PURPLE_MEDIA_VIDEO)), NULL);
|
|
2372 }
|
|
2373 }
|
|
2374 }
|
|
2375
|
|
2376 g_signal_emit(media, purple_media_signals[STREAM_INFO],
|
|
2377 0, type, session_id, participant, local);
|
|
2378
|
|
2379 if (type == PURPLE_MEDIA_INFO_HANGUP ||
|
|
2380 type == PURPLE_MEDIA_INFO_REJECT) {
|
|
2381 purple_media_end(media, session_id, participant);
|
|
2382 }
|
|
2383 #endif
|
|
2384 }
|
|
2385
|
|
2386 #ifdef USE_VV
|
|
2387 static void
|
|
2388 purple_media_new_local_candidate_cb(FsStream *stream,
|
|
2389 FsCandidate *local_candidate,
|
|
2390 PurpleMediaSession *session)
|
|
2391 {
|
|
2392 gchar *name;
|
|
2393 FsParticipant *participant;
|
|
2394 PurpleMediaCandidate *candidate;
|
|
2395
|
|
2396 g_return_if_fail(FS_IS_STREAM(stream));
|
|
2397 g_return_if_fail(session != NULL);
|
|
2398
|
|
2399 purple_debug_info("media", "got new local candidate: %s\n", local_candidate->foundation);
|
|
2400 g_object_get(stream, "participant", &participant, NULL);
|
|
2401 g_object_get(participant, "cname", &name, NULL);
|
|
2402 g_object_unref(participant);
|
|
2403
|
|
2404 purple_media_insert_local_candidate(session, name, fs_candidate_copy(local_candidate));
|
|
2405
|
|
2406 candidate = purple_media_candidate_from_fs(local_candidate);
|
|
2407 g_signal_emit(session->media, purple_media_signals[NEW_CANDIDATE],
|
|
2408 0, session->id, name, candidate);
|
|
2409 g_object_unref(candidate);
|
|
2410
|
|
2411 g_free(name);
|
|
2412 }
|
|
2413
|
|
2414 static void
|
|
2415 purple_media_candidates_prepared_cb(FsStream *stream, PurpleMediaSession *session)
|
|
2416 {
|
|
2417 gchar *name;
|
|
2418 FsParticipant *participant;
|
|
2419 PurpleMediaStream *stream_data;
|
|
2420
|
|
2421 g_return_if_fail(FS_IS_STREAM(stream));
|
|
2422 g_return_if_fail(session != NULL);
|
|
2423
|
|
2424 g_object_get(stream, "participant", &participant, NULL);
|
|
2425 g_object_get(participant, "cname", &name, NULL);
|
|
2426 g_object_unref(participant);
|
|
2427
|
|
2428 stream_data = purple_media_get_stream(session->media, session->id, name);
|
|
2429 stream_data->candidates_prepared = TRUE;
|
|
2430
|
|
2431 g_signal_emit(session->media,
|
|
2432 purple_media_signals[CANDIDATES_PREPARED],
|
|
2433 0, session->id, name);
|
|
2434
|
|
2435 g_free(name);
|
|
2436 }
|
|
2437
|
|
2438 /* callback called when a pair of transport candidates (local and remote)
|
|
2439 * has been established */
|
|
2440 static void
|
|
2441 purple_media_candidate_pair_established_cb(FsStream *fsstream,
|
|
2442 FsCandidate *native_candidate,
|
|
2443 FsCandidate *remote_candidate,
|
|
2444 PurpleMediaSession *session)
|
|
2445 {
|
|
2446 gchar *name;
|
|
2447 FsParticipant *participant;
|
|
2448 PurpleMediaStream *stream;
|
|
2449 GList *iter;
|
|
2450
|
|
2451 g_return_if_fail(FS_IS_STREAM(fsstream));
|
|
2452 g_return_if_fail(session != NULL);
|
|
2453
|
|
2454 g_object_get(fsstream, "participant", &participant, NULL);
|
|
2455 g_object_get(participant, "cname", &name, NULL);
|
|
2456 g_object_unref(participant);
|
|
2457
|
|
2458 stream = purple_media_get_stream(session->media, session->id, name);
|
|
2459
|
|
2460 iter = stream->active_local_candidates;
|
|
2461 for(; iter; iter = g_list_next(iter)) {
|
|
2462 FsCandidate *c = iter->data;
|
|
2463 if (native_candidate->component_id == c->component_id) {
|
|
2464 fs_candidate_destroy(c);
|
|
2465 stream->active_local_candidates =
|
|
2466 g_list_delete_link(iter, iter);
|
|
2467 stream->active_local_candidates = g_list_prepend(
|
|
2468 stream->active_local_candidates,
|
|
2469 fs_candidate_copy(native_candidate));
|
|
2470 break;
|
|
2471 }
|
|
2472 }
|
|
2473 if (iter == NULL)
|
|
2474 stream->active_local_candidates = g_list_prepend(
|
|
2475 stream->active_local_candidates,
|
|
2476 fs_candidate_copy(native_candidate));
|
|
2477
|
|
2478 iter = stream->active_remote_candidates;
|
|
2479 for(; iter; iter = g_list_next(iter)) {
|
|
2480 FsCandidate *c = iter->data;
|
|
2481 if (native_candidate->component_id == c->component_id) {
|
|
2482 fs_candidate_destroy(c);
|
|
2483 stream->active_remote_candidates =
|
|
2484 g_list_delete_link(iter, iter);
|
|
2485 stream->active_remote_candidates = g_list_prepend(
|
|
2486 stream->active_remote_candidates,
|
|
2487 fs_candidate_copy(remote_candidate));
|
|
2488 break;
|
|
2489 }
|
|
2490 }
|
|
2491 if (iter == NULL)
|
|
2492 stream->active_remote_candidates = g_list_prepend(
|
|
2493 stream->active_remote_candidates,
|
|
2494 fs_candidate_copy(remote_candidate));
|
|
2495
|
|
2496 purple_debug_info("media", "candidate pair established\n");
|
|
2497 }
|
|
2498
|
|
2499 static gboolean
|
|
2500 purple_media_connected_cb(PurpleMediaStream *stream)
|
|
2501 {
|
|
2502 g_return_val_if_fail(stream != NULL, FALSE);
|
|
2503
|
|
2504 stream->connected_cb_id = 0;
|
|
2505
|
|
2506 purple_media_manager_create_output_window(
|
|
2507 stream->session->media->priv->manager,
|
|
2508 stream->session->media,
|
|
2509 stream->session->id, stream->participant);
|
|
2510
|
|
2511 g_signal_emit(stream->session->media,
|
|
2512 purple_media_signals[STATE_CHANGED],
|
|
2513 0, PURPLE_MEDIA_STATE_CONNECTED,
|
|
2514 stream->session->id, stream->participant);
|
|
2515 return FALSE;
|
|
2516 }
|
|
2517
|
|
2518 static void
|
|
2519 purple_media_src_pad_added_cb(FsStream *fsstream, GstPad *srcpad,
|
|
2520 FsCodec *codec, PurpleMediaStream *stream)
|
|
2521 {
|
|
2522 PurpleMediaPrivate *priv;
|
|
2523 GstPad *sinkpad;
|
|
2524
|
|
2525 g_return_if_fail(FS_IS_STREAM(fsstream));
|
|
2526 g_return_if_fail(stream != NULL);
|
|
2527
|
|
2528 priv = stream->session->media->priv;
|
|
2529
|
|
2530 if (stream->src == NULL) {
|
|
2531 GstElement *sink = NULL;
|
|
2532
|
|
2533 if (codec->media_type == FS_MEDIA_TYPE_AUDIO) {
|
|
2534 GstElement *queue = NULL;
|
|
2535 double output_volume = purple_prefs_get_int(
|
|
2536 "/purple/media/audio/volume/output")/10.0;
|
|
2537 /*
|
|
2538 * Should this instead be:
|
|
2539 * audioconvert ! audioresample ! liveadder !
|
|
2540 * audioresample ! audioconvert ! realsink
|
|
2541 */
|
|
2542 queue = gst_element_factory_make("queue", NULL);
|
|
2543 stream->volume = gst_element_factory_make(
|
|
2544 "volume", NULL);
|
|
2545 g_object_set(stream->volume, "volume",
|
|
2546 output_volume, NULL);
|
|
2547 stream->level = gst_element_factory_make(
|
|
2548 "level", NULL);
|
|
2549 stream->src = gst_element_factory_make(
|
|
2550 "liveadder", NULL);
|
|
2551 sink = purple_media_manager_get_element(priv->manager,
|
|
2552 PURPLE_MEDIA_RECV_AUDIO,
|
|
2553 stream->session->media,
|
|
2554 stream->session->id,
|
|
2555 stream->participant);
|
|
2556 gst_bin_add(GST_BIN(priv->confbin), queue);
|
|
2557 gst_bin_add(GST_BIN(priv->confbin), stream->volume);
|
|
2558 gst_bin_add(GST_BIN(priv->confbin), stream->level);
|
|
2559 gst_bin_add(GST_BIN(priv->confbin), sink);
|
|
2560 gst_element_set_state(sink, GST_STATE_PLAYING);
|
|
2561 gst_element_set_state(stream->level, GST_STATE_PLAYING);
|
|
2562 gst_element_set_state(stream->volume, GST_STATE_PLAYING);
|
|
2563 gst_element_set_state(queue, GST_STATE_PLAYING);
|
|
2564 gst_element_link(stream->level, sink);
|
|
2565 gst_element_link(stream->volume, stream->level);
|
|
2566 gst_element_link(queue, stream->volume);
|
|
2567 sink = queue;
|
|
2568 } else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) {
|
|
2569 stream->src = gst_element_factory_make(
|
|
2570 "fsfunnel", NULL);
|
|
2571 sink = gst_element_factory_make(
|
|
2572 "fakesink", NULL);
|
|
2573 g_object_set(G_OBJECT(sink), "async", FALSE, NULL);
|
|
2574 gst_bin_add(GST_BIN(priv->confbin), sink);
|
|
2575 gst_element_set_state(sink, GST_STATE_PLAYING);
|
|
2576 }
|
|
2577 stream->tee = gst_element_factory_make("tee", NULL);
|
|
2578 gst_bin_add_many(GST_BIN(priv->confbin),
|
|
2579 stream->src, stream->tee, NULL);
|
|
2580 gst_element_set_state(stream->tee, GST_STATE_PLAYING);
|
|
2581 gst_element_set_state(stream->src, GST_STATE_PLAYING);
|
|
2582 gst_element_link_many(stream->src, stream->tee, sink, NULL);
|
|
2583 }
|
|
2584
|
|
2585 sinkpad = gst_element_get_request_pad(stream->src, "sink%d");
|
|
2586 gst_pad_link(srcpad, sinkpad);
|
|
2587 gst_object_unref(sinkpad);
|
|
2588
|
|
2589 stream->connected_cb_id = purple_timeout_add(0,
|
|
2590 (GSourceFunc)purple_media_connected_cb, stream);
|
|
2591 }
|
|
2592
|
|
2593 static void
|
|
2594 purple_media_element_added_cb(FsElementAddedNotifier *self,
|
|
2595 GstBin *bin, GstElement *element, gpointer user_data)
|
|
2596 {
|
|
2597 /*
|
|
2598 * Hack to make H264 work with Gmail video.
|
|
2599 */
|
|
2600 if (!strncmp(GST_ELEMENT_NAME(element), "x264", 4)) {
|
|
2601 g_object_set(GST_OBJECT(element), "cabac", FALSE, NULL);
|
|
2602 }
|
|
2603 }
|
|
2604 #endif /* USE_VV */
|
|
2605
|
|
2606 gboolean
|
|
2607 purple_media_add_stream(PurpleMedia *media, const gchar *sess_id,
|
|
2608 const gchar *who, PurpleMediaSessionType type,
|
|
2609 gboolean initiator, const gchar *transmitter,
|
|
2610 guint num_params, GParameter *params)
|
|
2611 {
|
|
2612 #ifdef USE_VV
|
|
2613 PurpleMediaSession *session;
|
|
2614 FsParticipant *participant = NULL;
|
|
2615 PurpleMediaStream *stream = NULL;
|
|
2616 FsMediaType media_type = purple_media_to_fs_media_type(type);
|
|
2617 FsStreamDirection type_direction =
|
|
2618 purple_media_to_fs_stream_direction(type);
|
|
2619 gboolean is_nice = !strcmp(transmitter, "nice");
|
|
2620
|
|
2621 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
|
|
2622
|
|
2623 session = purple_media_get_session(media, sess_id);
|
|
2624
|
|
2625 if (!session) {
|
|
2626 GError *err = NULL;
|
|
2627 GList *codec_conf = NULL, *iter = NULL;
|
|
2628 gchar *filename = NULL;
|
|
2629 PurpleMediaSessionType session_type;
|
|
2630 GstElement *src = NULL;
|
|
2631
|
|
2632 session = g_new0(PurpleMediaSession, 1);
|
|
2633
|
|
2634 session->session = fs_conference_new_session(
|
|
2635 media->priv->conference, media_type, &err);
|
|
2636
|
|
2637 if (err != NULL) {
|
|
2638 purple_media_error(media, _("Error creating session: %s"), err->message);
|
|
2639 g_error_free(err);
|
|
2640 g_free(session);
|
|
2641 return FALSE;
|
|
2642 }
|
|
2643
|
|
2644 filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
|
|
2645 codec_conf = fs_codec_list_from_keyfile(filename, &err);
|
|
2646 g_free(filename);
|
|
2647
|
|
2648 if (err != NULL) {
|
|
2649 if (err->code == 4)
|
|
2650 purple_debug_info("media", "Couldn't read "
|
|
2651 "fs-codec.conf: %s\n",
|
|
2652 err->message);
|
|
2653 else
|
|
2654 purple_debug_error("media", "Error reading "
|
|
2655 "fs-codec.conf: %s\n",
|
|
2656 err->message);
|
|
2657 g_error_free(err);
|
|
2658 }
|
|
2659
|
|
2660 /*
|
|
2661 * Add SPEEX if the configuration file doesn't exist or
|
|
2662 * there isn't a speex entry.
|
|
2663 */
|
|
2664 for (iter = codec_conf; iter; iter = g_list_next(iter)) {
|
|
2665 FsCodec *codec = iter->data;
|
|
2666 if (!g_ascii_strcasecmp(codec->encoding_name, "speex"))
|
|
2667 break;
|
|
2668 }
|
|
2669
|
|
2670 if (iter == NULL) {
|
|
2671 codec_conf = g_list_prepend(codec_conf,
|
|
2672 fs_codec_new(FS_CODEC_ID_ANY,
|
|
2673 "SPEEX", FS_MEDIA_TYPE_AUDIO, 8000));
|
|
2674 codec_conf = g_list_prepend(codec_conf,
|
|
2675 fs_codec_new(FS_CODEC_ID_ANY,
|
|
2676 "SPEEX", FS_MEDIA_TYPE_AUDIO, 16000));
|
|
2677 }
|
|
2678
|
|
2679 fs_session_set_codec_preferences(session->session, codec_conf, NULL);
|
|
2680
|
|
2681 /*
|
|
2682 * Removes a 5-7 second delay before
|
|
2683 * receiving the src-pad-added signal.
|
|
2684 * Only works for non-multicast FsRtpSessions.
|
|
2685 */
|
|
2686 if (is_nice || !strcmp(transmitter, "rawudp"))
|
|
2687 g_object_set(G_OBJECT(session->session),
|
|
2688 "no-rtcp-timeout", 0, NULL);
|
|
2689
|
|
2690 /*
|
|
2691 * Hack to make x264 work with Gmail video.
|
|
2692 */
|
|
2693 if (is_nice && !strcmp(sess_id, "google-video")) {
|
|
2694 FsElementAddedNotifier *notifier =
|
|
2695 fs_element_added_notifier_new();
|
|
2696 g_signal_connect(G_OBJECT(notifier), "element-added",
|
|
2697 G_CALLBACK(purple_media_element_added_cb),
|
|
2698 stream);
|
|
2699 fs_element_added_notifier_add(notifier,
|
|
2700 GST_BIN(media->priv->conference));
|
|
2701 }
|
|
2702
|
|
2703 fs_codec_list_destroy(codec_conf);
|
|
2704
|
|
2705 session->id = g_strdup(sess_id);
|
|
2706 session->media = media;
|
|
2707 session->type = type;
|
|
2708 session->initiator = initiator;
|
|
2709
|
|
2710 purple_media_add_session(media, session);
|
|
2711 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
|
|
2712 0, PURPLE_MEDIA_STATE_NEW,
|
|
2713 session->id, NULL);
|
|
2714
|
|
2715 if (type_direction & FS_DIRECTION_SEND) {
|
|
2716 session_type = purple_media_from_fs(media_type,
|
|
2717 FS_DIRECTION_SEND);
|
|
2718 src = purple_media_manager_get_element(
|
|
2719 media->priv->manager, session_type,
|
|
2720 media, session->id, who);
|
|
2721 if (!GST_IS_ELEMENT(src)) {
|
|
2722 purple_debug_error("media",
|
|
2723 "Error creating src for session %s\n",
|
|
2724 session->id);
|
|
2725 purple_media_end(media, session->id, NULL);
|
|
2726 return FALSE;
|
|
2727 }
|
|
2728
|
|
2729 purple_media_set_src(media, session->id, src);
|
|
2730 gst_element_set_state(session->src, GST_STATE_PLAYING);
|
|
2731 purple_media_manager_create_output_window(
|
|
2732 media->priv->manager,
|
|
2733 session->media,
|
|
2734 session->id, NULL);
|
|
2735 }
|
|
2736 }
|
|
2737
|
|
2738 if (!(participant = purple_media_add_participant(media, who))) {
|
|
2739 purple_media_remove_session(media, session);
|
|
2740 g_free(session);
|
|
2741 return FALSE;
|
|
2742 } else {
|
|
2743 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
|
|
2744 0, PURPLE_MEDIA_STATE_NEW,
|
|
2745 NULL, who);
|
|
2746 }
|
|
2747
|
|
2748 stream = purple_media_get_stream(media, sess_id, who);
|
|
2749
|
|
2750 if (!stream) {
|
|
2751 GError *err = NULL;
|
|
2752 FsStream *fsstream = NULL;
|
|
2753 const gchar *stun_ip = purple_network_get_stun_ip();
|
|
2754 const gchar *turn_ip = purple_network_get_turn_ip();
|
|
2755
|
|
2756 if (stun_ip || turn_ip) {
|
|
2757 guint new_num_params =
|
|
2758 (stun_ip && is_nice) && turn_ip ?
|
|
2759 num_params + 2 : num_params + 1;
|
|
2760 guint next_param_index = num_params;
|
|
2761 GParameter *param = g_new0(GParameter, new_num_params);
|
|
2762 memcpy(param, params, sizeof(GParameter) * num_params);
|
|
2763
|
|
2764 if (stun_ip) {
|
|
2765 purple_debug_info("media",
|
|
2766 "setting property stun-ip on new stream: %s\n", stun_ip);
|
|
2767
|
|
2768 param[next_param_index].name = "stun-ip";
|
|
2769 g_value_init(¶m[next_param_index].value, G_TYPE_STRING);
|
|
2770 g_value_set_string(¶m[next_param_index].value, stun_ip);
|
|
2771 next_param_index++;
|
|
2772 }
|
|
2773
|
|
2774 if (turn_ip && is_nice) {
|
|
2775 GValueArray *relay_info = g_value_array_new(0);
|
|
2776 GValue value;
|
|
2777 gint turn_port =
|
|
2778 purple_prefs_get_int("/purple/network/turn_port");
|
|
2779 const gchar *username =
|
|
2780 purple_prefs_get_string("/purple/network/turn_username");
|
|
2781 const gchar *password =
|
|
2782 purple_prefs_get_string("/purple/network/turn_password");
|
|
2783 GstStructure *turn_setup = gst_structure_new("relay-info",
|
|
2784 "ip", G_TYPE_STRING, turn_ip,
|
|
2785 "port", G_TYPE_UINT, turn_port,
|
|
2786 "username", G_TYPE_STRING, username,
|
|
2787 "password", G_TYPE_STRING, password,
|
|
2788 NULL);
|
|
2789
|
|
2790 if (turn_setup) {
|
|
2791 memset(&value, 0, sizeof(GValue));
|
|
2792 g_value_init(&value, GST_TYPE_STRUCTURE);
|
|
2793 gst_value_set_structure(&value, turn_setup);
|
|
2794 relay_info = g_value_array_append(relay_info, &value);
|
|
2795 gst_structure_free(turn_setup);
|
|
2796
|
|
2797 purple_debug_info("media",
|
|
2798 "setting property relay-info on new stream\n");
|
|
2799 param[next_param_index].name = "relay-info";
|
|
2800 g_value_init(¶m[next_param_index].value,
|
|
2801 G_TYPE_VALUE_ARRAY);
|
|
2802 g_value_set_boxed(¶m[next_param_index].value,
|
|
2803 relay_info);
|
|
2804 g_value_array_free(relay_info);
|
|
2805 } else {
|
|
2806 purple_debug_error("media", "Error relay info");
|
|
2807 g_object_unref(participant);
|
|
2808 g_hash_table_remove(media->priv->participants, who);
|
|
2809 purple_media_remove_session(media, session);
|
|
2810 g_free(session);
|
|
2811 return FALSE;
|
|
2812 }
|
|
2813 }
|
|
2814
|
|
2815 fsstream = fs_session_new_stream(session->session,
|
|
2816 participant, type_direction &
|
|
2817 FS_DIRECTION_RECV, transmitter,
|
|
2818 new_num_params, param, &err);
|
|
2819 g_free(param);
|
|
2820 } else {
|
|
2821 fsstream = fs_session_new_stream(session->session,
|
|
2822 participant, type_direction &
|
|
2823 FS_DIRECTION_RECV, transmitter,
|
|
2824 num_params, params, &err);
|
|
2825 }
|
|
2826
|
|
2827 if (fsstream == NULL) {
|
|
2828 purple_debug_error("media",
|
|
2829 "Error creating stream: %s\n",
|
|
2830 err && err->message ?
|
|
2831 err->message : "NULL");
|
|
2832 if (err)
|
|
2833 g_error_free(err);
|
|
2834 g_object_unref(participant);
|
|
2835 g_hash_table_remove(media->priv->participants, who);
|
|
2836 purple_media_remove_session(media, session);
|
|
2837 g_free(session);
|
|
2838 return FALSE;
|
|
2839 }
|
|
2840
|
|
2841 stream = purple_media_insert_stream(session, who, fsstream);
|
|
2842 stream->initiator = initiator;
|
|
2843
|
|
2844 /* callback for source pad added (new stream source ready) */
|
|
2845 g_signal_connect(G_OBJECT(fsstream),
|
|
2846 "src-pad-added", G_CALLBACK(purple_media_src_pad_added_cb), stream);
|
|
2847
|
|
2848 g_signal_emit(media, purple_media_signals[STATE_CHANGED],
|
|
2849 0, PURPLE_MEDIA_STATE_NEW,
|
|
2850 session->id, who);
|
|
2851 } else {
|
|
2852 if (purple_media_to_fs_stream_direction(stream->session->type)
|
|
2853 != type_direction) {
|
|
2854 /* change direction */
|
|
2855 g_object_set(stream->stream, "direction",
|
|
2856 type_direction, NULL);
|
|
2857 }
|
|
2858 }
|
|
2859
|
|
2860 return TRUE;
|
|
2861 #else
|
|
2862 return FALSE;
|
|
2863 #endif /* USE_VV */
|
|
2864 }
|
|
2865
|
|
2866 PurpleMediaManager *
|
|
2867 purple_media_get_manager(PurpleMedia *media)
|
|
2868 {
|
|
2869 PurpleMediaManager *ret;
|
|
2870 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
2871 g_object_get(media, "manager", &ret, NULL);
|
|
2872 return ret;
|
|
2873 }
|
|
2874
|
|
2875 PurpleMediaSessionType
|
|
2876 purple_media_get_session_type(PurpleMedia *media, const gchar *sess_id)
|
|
2877 {
|
|
2878 #ifdef USE_VV
|
|
2879 PurpleMediaSession *session;
|
|
2880 g_return_val_if_fail(PURPLE_IS_MEDIA(media), PURPLE_MEDIA_NONE);
|
|
2881 session = purple_media_get_session(media, sess_id);
|
|
2882 return session->type;
|
|
2883 #else
|
|
2884 return PURPLE_MEDIA_NONE;
|
|
2885 #endif
|
|
2886 }
|
|
2887 /* XXX: Should wait until codecs-ready is TRUE before using this function */
|
|
2888 GList *
|
|
2889 purple_media_get_codecs(PurpleMedia *media, const gchar *sess_id)
|
|
2890 {
|
|
2891 #ifdef USE_VV
|
|
2892 GList *fscodecs;
|
|
2893 GList *codecs;
|
|
2894 PurpleMediaSession *session;
|
|
2895
|
|
2896 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
2897
|
|
2898 session = purple_media_get_session(media, sess_id);
|
|
2899
|
|
2900 if (session == NULL)
|
|
2901 return NULL;
|
|
2902
|
|
2903 g_object_get(G_OBJECT(session->session),
|
|
2904 "codecs", &fscodecs, NULL);
|
|
2905 codecs = purple_media_codec_list_from_fs(fscodecs);
|
|
2906 fs_codec_list_destroy(fscodecs);
|
|
2907 return codecs;
|
|
2908 #else
|
|
2909 return NULL;
|
|
2910 #endif
|
|
2911 }
|
|
2912
|
|
2913 GList *
|
|
2914 purple_media_get_local_candidates(PurpleMedia *media, const gchar *sess_id,
|
|
2915 const gchar *participant)
|
|
2916 {
|
|
2917 #ifdef USE_VV
|
|
2918 PurpleMediaStream *stream;
|
|
2919 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
2920 stream = purple_media_get_stream(media, sess_id, participant);
|
|
2921 return stream ? purple_media_candidate_list_from_fs(
|
|
2922 stream->local_candidates) : NULL;
|
|
2923 #else
|
|
2924 return NULL;
|
|
2925 #endif
|
|
2926 }
|
|
2927
|
|
2928 void
|
|
2929 purple_media_add_remote_candidates(PurpleMedia *media, const gchar *sess_id,
|
|
2930 const gchar *participant,
|
|
2931 GList *remote_candidates)
|
|
2932 {
|
|
2933 #ifdef USE_VV
|
|
2934 PurpleMediaStream *stream;
|
|
2935 GError *err = NULL;
|
|
2936
|
|
2937 g_return_if_fail(PURPLE_IS_MEDIA(media));
|
|
2938 stream = purple_media_get_stream(media, sess_id, participant);
|
|
2939
|
|
2940 if (stream == NULL) {
|
|
2941 purple_debug_error("media",
|
|
2942 "purple_media_add_remote_candidates: "
|
|
2943 "couldn't find stream %s %s.\n",
|
|
2944 sess_id ? sess_id : "(null)",
|
|
2945 participant ? participant : "(null)");
|
|
2946 return;
|
|
2947 }
|
|
2948
|
|
2949 stream->remote_candidates = g_list_concat(stream->remote_candidates,
|
|
2950 purple_media_candidate_list_to_fs(remote_candidates));
|
|
2951
|
|
2952 if (stream->accepted == TRUE) {
|
|
2953 fs_stream_set_remote_candidates(stream->stream,
|
|
2954 stream->remote_candidates, &err);
|
|
2955
|
|
2956 if (err) {
|
|
2957 purple_debug_error("media", "Error adding remote"
|
|
2958 " candidates: %s\n", err->message);
|
|
2959 g_error_free(err);
|
|
2960 }
|
|
2961 }
|
|
2962 #endif
|
|
2963 }
|
|
2964
|
|
2965 #if 0
|
|
2966 /*
|
|
2967 * These two functions aren't being used and I'd rather not lock in the API
|
|
2968 * until they are needed. If they ever are.
|
|
2969 */
|
|
2970
|
|
2971 GList *
|
|
2972 purple_media_get_active_local_candidates(PurpleMedia *media,
|
|
2973 const gchar *sess_id, const gchar *participant)
|
|
2974 {
|
|
2975 #ifdef USE_VV
|
|
2976 PurpleMediaStream *stream;
|
|
2977 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
2978 stream = purple_media_get_stream(media, sess_id, participant);
|
|
2979 return purple_media_candidate_list_from_fs(
|
|
2980 stream->active_local_candidates);
|
|
2981 #else
|
|
2982 return NULL;
|
|
2983 #endif
|
|
2984 }
|
|
2985
|
|
2986 GList *
|
|
2987 purple_media_get_active_remote_candidates(PurpleMedia *media,
|
|
2988 const gchar *sess_id, const gchar *participant)
|
|
2989 {
|
|
2990 #ifdef USE_VV
|
|
2991 PurpleMediaStream *stream;
|
|
2992 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
2993 stream = purple_media_get_stream(media, sess_id, participant);
|
|
2994 return purple_media_candidate_list_from_fs(
|
|
2995 stream->active_remote_candidates);
|
|
2996 #else
|
|
2997 return NULL;
|
|
2998 #endif
|
|
2999 }
|
|
3000 #endif
|
|
3001
|
|
3002 gboolean
|
|
3003 purple_media_set_remote_codecs(PurpleMedia *media, const gchar *sess_id,
|
|
3004 const gchar *participant, GList *codecs)
|
|
3005 {
|
|
3006 #ifdef USE_VV
|
|
3007 PurpleMediaStream *stream;
|
|
3008 FsStream *fsstream;
|
|
3009 GList *fscodecs;
|
|
3010 GError *err = NULL;
|
|
3011
|
|
3012 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
|
|
3013 stream = purple_media_get_stream(media, sess_id, participant);
|
|
3014
|
|
3015 if (stream == NULL)
|
|
3016 return FALSE;
|
|
3017
|
|
3018 fsstream = stream->stream;
|
|
3019 fscodecs = purple_media_codec_list_to_fs(codecs);
|
|
3020 fs_stream_set_remote_codecs(fsstream, fscodecs, &err);
|
|
3021 fs_codec_list_destroy(fscodecs);
|
|
3022
|
|
3023 if (err) {
|
|
3024 purple_debug_error("media", "Error setting remote codecs: %s\n",
|
|
3025 err->message);
|
|
3026 g_error_free(err);
|
|
3027 return FALSE;
|
|
3028 }
|
|
3029 return TRUE;
|
|
3030 #else
|
|
3031 return FALSE;
|
|
3032 #endif
|
|
3033 }
|
|
3034
|
|
3035 gboolean
|
|
3036 purple_media_candidates_prepared(PurpleMedia *media,
|
|
3037 const gchar *session_id, const gchar *participant)
|
|
3038 {
|
|
3039 #ifdef USE_VV
|
|
3040 GList *streams;
|
|
3041 gboolean prepared = TRUE;
|
|
3042
|
|
3043 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
|
|
3044
|
|
3045 streams = purple_media_get_streams(media, session_id, participant);
|
|
3046
|
|
3047 for (; streams; streams = g_list_delete_link(streams, streams)) {
|
|
3048 PurpleMediaStream *stream = streams->data;
|
|
3049 if (stream->candidates_prepared == FALSE) {
|
|
3050 g_list_free(streams);
|
|
3051 prepared = FALSE;
|
|
3052 break;
|
|
3053 }
|
|
3054 }
|
|
3055
|
|
3056 return prepared;
|
|
3057 #else
|
|
3058 return FALSE;
|
|
3059 #endif
|
|
3060 }
|
|
3061
|
|
3062 gboolean
|
|
3063 purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, PurpleMediaCodec *codec)
|
|
3064 {
|
|
3065 #ifdef USE_VV
|
|
3066 PurpleMediaSession *session;
|
|
3067 FsCodec *fscodec;
|
|
3068 GError *err = NULL;
|
|
3069
|
|
3070 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
|
|
3071
|
|
3072 session = purple_media_get_session(media, sess_id);
|
|
3073
|
|
3074 if (session != NULL)
|
|
3075 return FALSE;
|
|
3076
|
|
3077 fscodec = purple_media_codec_to_fs(codec);
|
|
3078 fs_session_set_send_codec(session->session, fscodec, &err);
|
|
3079 fs_codec_destroy(fscodec);
|
|
3080
|
|
3081 if (err) {
|
|
3082 purple_debug_error("media", "Error setting send codec\n");
|
|
3083 g_error_free(err);
|
|
3084 return FALSE;
|
|
3085 }
|
|
3086 return TRUE;
|
|
3087 #else
|
|
3088 return FALSE;
|
|
3089 #endif
|
|
3090 }
|
|
3091
|
|
3092 gboolean
|
|
3093 purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id)
|
|
3094 {
|
|
3095 #ifdef USE_VV
|
|
3096 gboolean ret;
|
|
3097
|
|
3098 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
|
|
3099
|
|
3100 if (sess_id != NULL) {
|
|
3101 PurpleMediaSession *session;
|
|
3102 session = purple_media_get_session(media, sess_id);
|
|
3103
|
|
3104 if (session == NULL)
|
|
3105 return FALSE;
|
|
3106 if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
|
|
3107 PURPLE_MEDIA_SEND_VIDEO))
|
|
3108 g_object_get(session->session,
|
|
3109 "codecs-ready", &ret, NULL);
|
|
3110 else
|
|
3111 ret = TRUE;
|
|
3112 } else {
|
|
3113 GList *values = g_hash_table_get_values(media->priv->sessions);
|
|
3114 for (; values; values = g_list_delete_link(values, values)) {
|
|
3115 PurpleMediaSession *session = values->data;
|
|
3116 if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
|
|
3117 PURPLE_MEDIA_SEND_VIDEO))
|
|
3118 g_object_get(session->session,
|
|
3119 "codecs-ready", &ret, NULL);
|
|
3120 else
|
|
3121 ret = TRUE;
|
|
3122
|
|
3123 if (ret == FALSE)
|
|
3124 break;
|
|
3125 }
|
|
3126 if (values != NULL)
|
|
3127 g_list_free(values);
|
|
3128 }
|
|
3129 return ret;
|
|
3130 #else
|
|
3131 return FALSE;
|
|
3132 #endif
|
|
3133 }
|
|
3134
|
|
3135 gboolean
|
|
3136 purple_media_is_initiator(PurpleMedia *media,
|
|
3137 const gchar *sess_id, const gchar *participant)
|
|
3138 {
|
|
3139 #ifdef USE_VV
|
|
3140 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
|
|
3141
|
|
3142 if (sess_id == NULL && participant == NULL)
|
|
3143 return media->priv->initiator;
|
|
3144 else if (sess_id != NULL && participant == NULL) {
|
|
3145 PurpleMediaSession *session =
|
|
3146 purple_media_get_session(media, sess_id);
|
|
3147 return session != NULL ? session->initiator : FALSE;
|
|
3148 } else if (sess_id != NULL && participant != NULL) {
|
|
3149 PurpleMediaStream *stream = purple_media_get_stream(
|
|
3150 media, sess_id, participant);
|
|
3151 return stream != NULL ? stream->initiator : FALSE;
|
|
3152 }
|
|
3153 #endif
|
|
3154 return FALSE;
|
|
3155 }
|
|
3156
|
|
3157 gboolean
|
|
3158 purple_media_accepted(PurpleMedia *media, const gchar *sess_id,
|
|
3159 const gchar *participant)
|
|
3160 {
|
|
3161 #ifdef USE_VV
|
|
3162 gboolean accepted = TRUE;
|
|
3163
|
|
3164 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
|
|
3165
|
|
3166 if (sess_id == NULL && participant == NULL) {
|
|
3167 GList *streams = media->priv->streams;
|
|
3168
|
|
3169 for (; streams; streams = g_list_next(streams)) {
|
|
3170 PurpleMediaStream *stream = streams->data;
|
|
3171 if (stream->accepted == FALSE) {
|
|
3172 accepted = FALSE;
|
|
3173 break;
|
|
3174 }
|
|
3175 }
|
|
3176 } else if (sess_id != NULL && participant == NULL) {
|
|
3177 GList *streams = purple_media_get_streams(
|
|
3178 media, sess_id, NULL);
|
|
3179 for (; streams; streams =
|
|
3180 g_list_delete_link(streams, streams)) {
|
|
3181 PurpleMediaStream *stream = streams->data;
|
|
3182 if (stream->accepted == FALSE) {
|
|
3183 g_list_free(streams);
|
|
3184 accepted = FALSE;
|
|
3185 break;
|
|
3186 }
|
|
3187 }
|
|
3188 } else if (sess_id != NULL && participant != NULL) {
|
|
3189 PurpleMediaStream *stream = purple_media_get_stream(
|
|
3190 media, sess_id, participant);
|
|
3191 if (stream == NULL || stream->accepted == FALSE)
|
|
3192 accepted = FALSE;
|
|
3193 }
|
|
3194
|
|
3195 return accepted;
|
|
3196 #else
|
|
3197 return FALSE;
|
|
3198 #endif
|
|
3199 }
|
|
3200
|
|
3201 void purple_media_set_input_volume(PurpleMedia *media,
|
|
3202 const gchar *session_id, double level)
|
|
3203 {
|
|
3204 #ifdef USE_VV
|
|
3205 GList *sessions;
|
|
3206
|
|
3207 g_return_if_fail(PURPLE_IS_MEDIA(media));
|
|
3208
|
|
3209 purple_prefs_set_int("/purple/media/audio/volume/input", level);
|
|
3210
|
|
3211 if (session_id == NULL)
|
|
3212 sessions = g_hash_table_get_values(media->priv->sessions);
|
|
3213 else
|
|
3214 sessions = g_list_append(NULL,
|
|
3215 purple_media_get_session(media, session_id));
|
|
3216
|
|
3217 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
|
|
3218 PurpleMediaSession *session = sessions->data;
|
|
3219
|
|
3220 if (session->type & PURPLE_MEDIA_SEND_AUDIO) {
|
|
3221 gchar *name = g_strdup_printf("volume_%s",
|
|
3222 session->id);
|
|
3223 GstElement *volume = gst_bin_get_by_name(
|
|
3224 GST_BIN(session->media->priv->confbin),
|
|
3225 name);
|
|
3226 g_free(name);
|
|
3227 g_object_set(volume, "volume", level/10.0, NULL);
|
|
3228 }
|
|
3229 }
|
|
3230 #endif
|
|
3231 }
|
|
3232
|
|
3233 void purple_media_set_output_volume(PurpleMedia *media,
|
|
3234 const gchar *session_id, const gchar *participant,
|
|
3235 double level)
|
|
3236 {
|
|
3237 #ifdef USE_VV
|
|
3238 GList *streams;
|
|
3239
|
|
3240 g_return_if_fail(PURPLE_IS_MEDIA(media));
|
|
3241
|
|
3242 purple_prefs_set_int("/purple/media/audio/volume/output", level);
|
|
3243
|
|
3244 streams = purple_media_get_streams(media,
|
|
3245 session_id, participant);
|
|
3246
|
|
3247 for (; streams; streams = g_list_delete_link(streams, streams)) {
|
|
3248 PurpleMediaStream *stream = streams->data;
|
|
3249
|
|
3250 if (stream->session->type & PURPLE_MEDIA_RECV_AUDIO
|
|
3251 && GST_IS_ELEMENT(stream->volume)) {
|
|
3252 g_object_set(stream->volume, "volume", level/10.0, NULL);
|
|
3253 }
|
|
3254 }
|
|
3255 #endif
|
|
3256 }
|
|
3257
|
|
3258 gulong
|
|
3259 purple_media_set_output_window(PurpleMedia *media, const gchar *session_id,
|
|
3260 const gchar *participant, gulong window_id)
|
|
3261 {
|
|
3262 #ifdef USE_VV
|
|
3263 g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE);
|
|
3264
|
|
3265 return purple_media_manager_set_output_window(media->priv->manager,
|
|
3266 media, session_id, participant, window_id);
|
|
3267 #else
|
|
3268 return 0;
|
|
3269 #endif
|
|
3270 }
|
|
3271
|
|
3272 void
|
|
3273 purple_media_remove_output_windows(PurpleMedia *media)
|
|
3274 {
|
|
3275 #ifdef USE_VV
|
|
3276 GList *iter = media->priv->streams;
|
|
3277 for (; iter; iter = g_list_next(iter)) {
|
|
3278 PurpleMediaStream *stream = iter->data;
|
|
3279 purple_media_manager_remove_output_windows(
|
|
3280 media->priv->manager, media,
|
|
3281 stream->session->id, stream->participant);
|
|
3282 }
|
|
3283
|
|
3284 iter = purple_media_get_session_ids(media);
|
|
3285 for (; iter; iter = g_list_delete_link(iter, iter)) {
|
|
3286 gchar *session_name = iter->data;
|
|
3287 purple_media_manager_remove_output_windows(
|
|
3288 media->priv->manager, media,
|
|
3289 session_name, NULL);
|
|
3290 }
|
|
3291 #endif
|
|
3292 }
|
|
3293
|
|
3294 #ifdef USE_GSTREAMER
|
|
3295 GstElement *
|
|
3296 purple_media_get_tee(PurpleMedia *media,
|
|
3297 const gchar *session_id, const gchar *participant)
|
|
3298 {
|
|
3299 #ifdef USE_VV
|
|
3300 g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL);
|
|
3301
|
|
3302 if (session_id != NULL && participant == NULL) {
|
|
3303 PurpleMediaSession *session =
|
|
3304 purple_media_get_session(media, session_id);
|
|
3305 return (session != NULL) ? session->tee : NULL;
|
|
3306 } else if (session_id != NULL && participant != NULL) {
|
|
3307 PurpleMediaStream *stream =
|
|
3308 purple_media_get_stream(media,
|
|
3309 session_id, participant);
|
|
3310 return (stream != NULL) ? stream->tee : NULL;
|
|
3311 }
|
|
3312 g_return_val_if_reached(NULL);
|
|
3313 #else
|
|
3314 return NULL;
|
|
3315 #endif
|
|
3316 }
|
|
3317 #endif /* USE_GSTREAMER */
|
|
3318
|