Mercurial > pidgin.yaz
comparison libpurple/media/backend-fs2.c @ 29775:e446b56c01e4
propagate from branch 'im.pidgin.pidgin' (head be77df4826034ac2521177bb6af6b60be3d1b4c4)
to branch 'im.pidgin.pidgin.next.minor' (head c0803026336ad6c803c2982ec7b0153378d71860)
author | Paul Aurich <paul@darkrain42.org> |
---|---|
date | Tue, 16 Feb 2010 15:16:44 +0000 |
parents | 1876a447db11 |
children | e75d6a51a5c4 |
comparison
equal
deleted
inserted
replaced
29454:5c9c4557fec2 | 29775:e446b56c01e4 |
---|---|
1 /** | |
2 * @file backend-fs2.c Farsight 2 backend for 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 "internal.h" | |
28 | |
29 #ifdef USE_VV | |
30 #include "backend-fs2.h" | |
31 | |
32 #include "backend-iface.h" | |
33 #include "debug.h" | |
34 #include "network.h" | |
35 #include "media-gst.h" | |
36 | |
37 #include <gst/farsight/fs-conference-iface.h> | |
38 #include <gst/farsight/fs-element-added-notifier.h> | |
39 | |
40 /** @copydoc _PurpleMediaBackendFs2Class */ | |
41 typedef struct _PurpleMediaBackendFs2Class PurpleMediaBackendFs2Class; | |
42 /** @copydoc _PurpleMediaBackendFs2Private */ | |
43 typedef struct _PurpleMediaBackendFs2Private PurpleMediaBackendFs2Private; | |
44 /** @copydoc _PurpleMediaBackendFs2Session */ | |
45 typedef struct _PurpleMediaBackendFs2Session PurpleMediaBackendFs2Session; | |
46 /** @copydoc _PurpleMediaBackendFs2Stream */ | |
47 typedef struct _PurpleMediaBackendFs2Stream PurpleMediaBackendFs2Stream; | |
48 | |
49 #define PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj) \ | |
50 (G_TYPE_INSTANCE_GET_PRIVATE((obj), \ | |
51 PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2Private)) | |
52 | |
53 static void purple_media_backend_iface_init(PurpleMediaBackendIface *iface); | |
54 | |
55 static gboolean | |
56 gst_bus_cb(GstBus *bus, GstMessage *msg, PurpleMediaBackendFs2 *self); | |
57 static void | |
58 state_changed_cb(PurpleMedia *media, PurpleMediaState state, | |
59 gchar *sid, gchar *name, PurpleMediaBackendFs2 *self); | |
60 static void | |
61 stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, | |
62 gchar *sid, gchar *name, gboolean local, | |
63 PurpleMediaBackendFs2 *self); | |
64 | |
65 static gboolean purple_media_backend_fs2_add_stream(PurpleMediaBackend *self, | |
66 const gchar *sess_id, const gchar *who, | |
67 PurpleMediaSessionType type, gboolean initiator, | |
68 const gchar *transmitter, | |
69 guint num_params, GParameter *params); | |
70 static void purple_media_backend_fs2_add_remote_candidates( | |
71 PurpleMediaBackend *self, | |
72 const gchar *sess_id, const gchar *participant, | |
73 GList *remote_candidates); | |
74 static gboolean purple_media_backend_fs2_codecs_ready(PurpleMediaBackend *self, | |
75 const gchar *sess_id); | |
76 static GList *purple_media_backend_fs2_get_codecs(PurpleMediaBackend *self, | |
77 const gchar *sess_id); | |
78 static GList *purple_media_backend_fs2_get_local_candidates( | |
79 PurpleMediaBackend *self, | |
80 const gchar *sess_id, const gchar *participant); | |
81 static gboolean purple_media_backend_fs2_set_remote_codecs( | |
82 PurpleMediaBackend *self, | |
83 const gchar *sess_id, const gchar *participant, | |
84 GList *codecs); | |
85 static gboolean purple_media_backend_fs2_set_send_codec( | |
86 PurpleMediaBackend *self, const gchar *sess_id, | |
87 PurpleMediaCodec *codec); | |
88 | |
89 struct _PurpleMediaBackendFs2Class | |
90 { | |
91 GObjectClass parent_class; | |
92 }; | |
93 | |
94 struct _PurpleMediaBackendFs2 | |
95 { | |
96 GObject parent; | |
97 }; | |
98 | |
99 G_DEFINE_TYPE_WITH_CODE(PurpleMediaBackendFs2, purple_media_backend_fs2, | |
100 G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE( | |
101 PURPLE_TYPE_MEDIA_BACKEND, purple_media_backend_iface_init)); | |
102 | |
103 struct _PurpleMediaBackendFs2Stream | |
104 { | |
105 PurpleMediaBackendFs2Session *session; | |
106 gchar *participant; | |
107 FsStream *stream; | |
108 | |
109 GstElement *src; | |
110 GstElement *tee; | |
111 GstElement *volume; | |
112 GstElement *level; | |
113 | |
114 GList *local_candidates; | |
115 GList *remote_candidates; | |
116 | |
117 guint connected_cb_id; | |
118 }; | |
119 | |
120 struct _PurpleMediaBackendFs2Session | |
121 { | |
122 PurpleMediaBackendFs2 *backend; | |
123 gchar *id; | |
124 FsSession *session; | |
125 | |
126 GstElement *src; | |
127 GstElement *tee; | |
128 | |
129 PurpleMediaSessionType type; | |
130 }; | |
131 | |
132 struct _PurpleMediaBackendFs2Private | |
133 { | |
134 PurpleMedia *media; | |
135 GstElement *confbin; | |
136 FsConference *conference; | |
137 gchar *conference_type; | |
138 | |
139 GHashTable *sessions; | |
140 GHashTable *participants; | |
141 | |
142 GList *streams; | |
143 }; | |
144 | |
145 enum { | |
146 PROP_0, | |
147 PROP_CONFERENCE_TYPE, | |
148 PROP_MEDIA, | |
149 }; | |
150 | |
151 static void | |
152 purple_media_backend_fs2_init(PurpleMediaBackendFs2 *self) | |
153 { | |
154 } | |
155 | |
156 static void | |
157 purple_media_backend_fs2_dispose(GObject *obj) | |
158 { | |
159 PurpleMediaBackendFs2Private *priv = | |
160 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj); | |
161 GList *iter = NULL; | |
162 | |
163 purple_debug_info("backend-fs2", "purple_media_backend_fs2_dispose\n"); | |
164 | |
165 if (priv->confbin) { | |
166 GstElement *pipeline; | |
167 | |
168 pipeline = purple_media_manager_get_pipeline( | |
169 purple_media_get_manager(priv->media)); | |
170 | |
171 gst_element_set_locked_state(priv->confbin, TRUE); | |
172 gst_element_set_state(GST_ELEMENT(priv->confbin), | |
173 GST_STATE_NULL); | |
174 | |
175 if (pipeline) { | |
176 GstBus *bus; | |
177 gst_bin_remove(GST_BIN(pipeline), priv->confbin); | |
178 bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); | |
179 g_signal_handlers_disconnect_matched(G_OBJECT(bus), | |
180 G_SIGNAL_MATCH_FUNC | | |
181 G_SIGNAL_MATCH_DATA, | |
182 0, 0, 0, gst_bus_cb, obj); | |
183 gst_object_unref(bus); | |
184 } else { | |
185 purple_debug_warning("backend-fs2", "Unable to " | |
186 "properly dispose the conference. " | |
187 "Couldn't get the pipeline.\n"); | |
188 } | |
189 | |
190 priv->confbin = NULL; | |
191 priv->conference = NULL; | |
192 | |
193 } | |
194 | |
195 if (priv->sessions) { | |
196 GList *sessions = g_hash_table_get_values(priv->sessions); | |
197 | |
198 for (; sessions; sessions = | |
199 g_list_delete_link(sessions, sessions)) { | |
200 PurpleMediaBackendFs2Session *session = | |
201 sessions->data; | |
202 | |
203 if (session->session) { | |
204 g_object_unref(session->session); | |
205 session->session = NULL; | |
206 } | |
207 } | |
208 } | |
209 | |
210 if (priv->participants) { | |
211 GList *participants = | |
212 g_hash_table_get_values(priv->participants); | |
213 for (; participants; participants = g_list_delete_link( | |
214 participants, participants)) | |
215 g_object_unref(participants->data); | |
216 priv->participants = NULL; | |
217 } | |
218 | |
219 for (iter = priv->streams; iter; iter = g_list_next(iter)) { | |
220 PurpleMediaBackendFs2Stream *stream = iter->data; | |
221 if (stream->stream) { | |
222 g_object_unref(stream->stream); | |
223 stream->stream = NULL; | |
224 } | |
225 } | |
226 | |
227 if (priv->media) { | |
228 g_object_remove_weak_pointer(G_OBJECT(priv->media), | |
229 (gpointer*)&priv->media); | |
230 priv->media = NULL; | |
231 } | |
232 | |
233 G_OBJECT_CLASS(purple_media_backend_fs2_parent_class)->dispose(obj); | |
234 } | |
235 | |
236 static void | |
237 purple_media_backend_fs2_finalize(GObject *obj) | |
238 { | |
239 PurpleMediaBackendFs2Private *priv = | |
240 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj); | |
241 | |
242 purple_debug_info("backend-fs2", "purple_media_backend_fs2_finalize\n"); | |
243 | |
244 g_free(priv->conference_type); | |
245 | |
246 for (; priv->streams; priv->streams = | |
247 g_list_delete_link(priv->streams, priv->streams)) { | |
248 PurpleMediaBackendFs2Stream *stream = priv->streams->data; | |
249 | |
250 /* Remove the connected_cb timeout */ | |
251 if (stream->connected_cb_id != 0) | |
252 purple_timeout_remove(stream->connected_cb_id); | |
253 | |
254 g_free(stream->participant); | |
255 | |
256 if (stream->local_candidates) | |
257 fs_candidate_list_destroy(stream->local_candidates); | |
258 | |
259 if (stream->remote_candidates) | |
260 fs_candidate_list_destroy(stream->remote_candidates); | |
261 | |
262 g_free(stream); | |
263 } | |
264 | |
265 if (priv->sessions) { | |
266 GList *sessions = g_hash_table_get_values(priv->sessions); | |
267 | |
268 for (; sessions; sessions = | |
269 g_list_delete_link(sessions, sessions)) { | |
270 PurpleMediaBackendFs2Session *session = | |
271 sessions->data; | |
272 g_free(session->id); | |
273 g_free(session); | |
274 } | |
275 | |
276 g_hash_table_destroy(priv->sessions); | |
277 } | |
278 | |
279 G_OBJECT_CLASS(purple_media_backend_fs2_parent_class)->finalize(obj); | |
280 } | |
281 | |
282 static void | |
283 purple_media_backend_fs2_set_property(GObject *object, guint prop_id, | |
284 const GValue *value, GParamSpec *pspec) | |
285 { | |
286 PurpleMediaBackendFs2Private *priv; | |
287 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object)); | |
288 | |
289 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(object); | |
290 | |
291 switch (prop_id) { | |
292 case PROP_CONFERENCE_TYPE: | |
293 priv->conference_type = g_value_dup_string(value); | |
294 break; | |
295 case PROP_MEDIA: | |
296 priv->media = g_value_get_object(value); | |
297 | |
298 if (priv->media == NULL) | |
299 break; | |
300 | |
301 g_object_add_weak_pointer(G_OBJECT(priv->media), | |
302 (gpointer*)&priv->media); | |
303 | |
304 g_signal_connect(G_OBJECT(priv->media), | |
305 "state-changed", | |
306 G_CALLBACK(state_changed_cb), | |
307 PURPLE_MEDIA_BACKEND_FS2(object)); | |
308 g_signal_connect(G_OBJECT(priv->media), "stream-info", | |
309 G_CALLBACK(stream_info_cb), | |
310 PURPLE_MEDIA_BACKEND_FS2(object)); | |
311 break; | |
312 default: | |
313 G_OBJECT_WARN_INVALID_PROPERTY_ID( | |
314 object, prop_id, pspec); | |
315 break; | |
316 } | |
317 } | |
318 | |
319 static void | |
320 purple_media_backend_fs2_get_property(GObject *object, guint prop_id, | |
321 GValue *value, GParamSpec *pspec) | |
322 { | |
323 PurpleMediaBackendFs2Private *priv; | |
324 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object)); | |
325 | |
326 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(object); | |
327 | |
328 switch (prop_id) { | |
329 case PROP_CONFERENCE_TYPE: | |
330 g_value_set_string(value, priv->conference_type); | |
331 break; | |
332 case PROP_MEDIA: | |
333 g_value_set_object(value, priv->media); | |
334 break; | |
335 default: | |
336 G_OBJECT_WARN_INVALID_PROPERTY_ID( | |
337 object, prop_id, pspec); | |
338 break; | |
339 } | |
340 } | |
341 | |
342 static void | |
343 purple_media_backend_fs2_class_init(PurpleMediaBackendFs2Class *klass) | |
344 { | |
345 GObjectClass *gobject_class = (GObjectClass*)klass; | |
346 | |
347 gobject_class->dispose = purple_media_backend_fs2_dispose; | |
348 gobject_class->finalize = purple_media_backend_fs2_finalize; | |
349 gobject_class->set_property = purple_media_backend_fs2_set_property; | |
350 gobject_class->get_property = purple_media_backend_fs2_get_property; | |
351 | |
352 g_object_class_override_property(gobject_class, PROP_CONFERENCE_TYPE, | |
353 "conference-type"); | |
354 g_object_class_override_property(gobject_class, PROP_MEDIA, "media"); | |
355 | |
356 g_type_class_add_private(klass, sizeof(PurpleMediaBackendFs2Private)); | |
357 } | |
358 | |
359 static void | |
360 purple_media_backend_iface_init(PurpleMediaBackendIface *iface) | |
361 { | |
362 iface->add_stream = purple_media_backend_fs2_add_stream; | |
363 iface->add_remote_candidates = | |
364 purple_media_backend_fs2_add_remote_candidates; | |
365 iface->codecs_ready = purple_media_backend_fs2_codecs_ready; | |
366 iface->get_codecs = purple_media_backend_fs2_get_codecs; | |
367 iface->get_local_candidates = | |
368 purple_media_backend_fs2_get_local_candidates; | |
369 iface->set_remote_codecs = purple_media_backend_fs2_set_remote_codecs; | |
370 iface->set_send_codec = purple_media_backend_fs2_set_send_codec; | |
371 } | |
372 | |
373 static FsMediaType | |
374 session_type_to_fs_media_type(PurpleMediaSessionType type) | |
375 { | |
376 if (type & PURPLE_MEDIA_AUDIO) | |
377 return FS_MEDIA_TYPE_AUDIO; | |
378 else if (type & PURPLE_MEDIA_VIDEO) | |
379 return FS_MEDIA_TYPE_VIDEO; | |
380 else | |
381 return 0; | |
382 } | |
383 | |
384 static FsStreamDirection | |
385 session_type_to_fs_stream_direction(PurpleMediaSessionType type) | |
386 { | |
387 if ((type & PURPLE_MEDIA_AUDIO) == PURPLE_MEDIA_AUDIO || | |
388 (type & PURPLE_MEDIA_VIDEO) == PURPLE_MEDIA_VIDEO) | |
389 return FS_DIRECTION_BOTH; | |
390 else if ((type & PURPLE_MEDIA_SEND_AUDIO) || | |
391 (type & PURPLE_MEDIA_SEND_VIDEO)) | |
392 return FS_DIRECTION_SEND; | |
393 else if ((type & PURPLE_MEDIA_RECV_AUDIO) || | |
394 (type & PURPLE_MEDIA_RECV_VIDEO)) | |
395 return FS_DIRECTION_RECV; | |
396 else | |
397 return FS_DIRECTION_NONE; | |
398 } | |
399 | |
400 static PurpleMediaSessionType | |
401 session_type_from_fs(FsMediaType type, FsStreamDirection direction) | |
402 { | |
403 PurpleMediaSessionType result = PURPLE_MEDIA_NONE; | |
404 if (type == FS_MEDIA_TYPE_AUDIO) { | |
405 if (direction & FS_DIRECTION_SEND) | |
406 result |= PURPLE_MEDIA_SEND_AUDIO; | |
407 if (direction & FS_DIRECTION_RECV) | |
408 result |= PURPLE_MEDIA_RECV_AUDIO; | |
409 } else if (type == FS_MEDIA_TYPE_VIDEO) { | |
410 if (direction & FS_DIRECTION_SEND) | |
411 result |= PURPLE_MEDIA_SEND_VIDEO; | |
412 if (direction & FS_DIRECTION_RECV) | |
413 result |= PURPLE_MEDIA_RECV_VIDEO; | |
414 } | |
415 return result; | |
416 } | |
417 | |
418 static FsCandidate * | |
419 candidate_to_fs(PurpleMediaCandidate *candidate) | |
420 { | |
421 FsCandidate *fscandidate; | |
422 gchar *foundation; | |
423 guint component_id; | |
424 gchar *ip; | |
425 guint port; | |
426 gchar *base_ip; | |
427 guint base_port; | |
428 PurpleMediaNetworkProtocol proto; | |
429 guint32 priority; | |
430 PurpleMediaCandidateType type; | |
431 gchar *username; | |
432 gchar *password; | |
433 guint ttl; | |
434 | |
435 if (candidate == NULL) | |
436 return NULL; | |
437 | |
438 g_object_get(G_OBJECT(candidate), | |
439 "foundation", &foundation, | |
440 "component-id", &component_id, | |
441 "ip", &ip, | |
442 "port", &port, | |
443 "base-ip", &base_ip, | |
444 "base-port", &base_port, | |
445 "protocol", &proto, | |
446 "priority", &priority, | |
447 "type", &type, | |
448 "username", &username, | |
449 "password", &password, | |
450 "ttl", &ttl, | |
451 NULL); | |
452 | |
453 fscandidate = fs_candidate_new(foundation, | |
454 component_id, type, | |
455 proto, ip, port); | |
456 | |
457 fscandidate->base_ip = base_ip; | |
458 fscandidate->base_port = base_port; | |
459 fscandidate->priority = priority; | |
460 fscandidate->username = username; | |
461 fscandidate->password = password; | |
462 fscandidate->ttl = ttl; | |
463 | |
464 g_free(foundation); | |
465 g_free(ip); | |
466 return fscandidate; | |
467 } | |
468 | |
469 static GList * | |
470 candidate_list_to_fs(GList *candidates) | |
471 { | |
472 GList *new_list = NULL; | |
473 | |
474 for (; candidates; candidates = g_list_next(candidates)) { | |
475 new_list = g_list_prepend(new_list, | |
476 candidate_to_fs(candidates->data)); | |
477 } | |
478 | |
479 new_list = g_list_reverse(new_list); | |
480 return new_list; | |
481 } | |
482 | |
483 static PurpleMediaCandidate * | |
484 candidate_from_fs(FsCandidate *fscandidate) | |
485 { | |
486 PurpleMediaCandidate *candidate; | |
487 | |
488 if (fscandidate == NULL) | |
489 return NULL; | |
490 | |
491 candidate = purple_media_candidate_new(fscandidate->foundation, | |
492 fscandidate->component_id, fscandidate->type, | |
493 fscandidate->proto, fscandidate->ip, fscandidate->port); | |
494 g_object_set(candidate, | |
495 "base-ip", fscandidate->base_ip, | |
496 "base-port", fscandidate->base_port, | |
497 "priority", fscandidate->priority, | |
498 "username", fscandidate->username, | |
499 "password", fscandidate->password, | |
500 "ttl", fscandidate->ttl, NULL); | |
501 return candidate; | |
502 } | |
503 | |
504 static GList * | |
505 candidate_list_from_fs(GList *candidates) | |
506 { | |
507 GList *new_list = NULL; | |
508 | |
509 for (; candidates; candidates = g_list_next(candidates)) { | |
510 new_list = g_list_prepend(new_list, | |
511 candidate_from_fs(candidates->data)); | |
512 } | |
513 | |
514 new_list = g_list_reverse(new_list); | |
515 return new_list; | |
516 } | |
517 | |
518 static FsCodec * | |
519 codec_to_fs(const PurpleMediaCodec *codec) | |
520 { | |
521 FsCodec *new_codec; | |
522 gint id; | |
523 char *encoding_name; | |
524 PurpleMediaSessionType media_type; | |
525 guint clock_rate; | |
526 guint channels; | |
527 GList *iter; | |
528 | |
529 if (codec == NULL) | |
530 return NULL; | |
531 | |
532 g_object_get(G_OBJECT(codec), | |
533 "id", &id, | |
534 "encoding-name", &encoding_name, | |
535 "media-type", &media_type, | |
536 "clock-rate", &clock_rate, | |
537 "channels", &channels, | |
538 "optional-params", &iter, | |
539 NULL); | |
540 | |
541 new_codec = fs_codec_new(id, encoding_name, | |
542 session_type_to_fs_media_type(media_type), | |
543 clock_rate); | |
544 new_codec->channels = channels; | |
545 | |
546 for (; iter; iter = g_list_next(iter)) { | |
547 PurpleKeyValuePair *param = (PurpleKeyValuePair*)iter->data; | |
548 fs_codec_add_optional_parameter(new_codec, | |
549 param->key, param->value); | |
550 } | |
551 | |
552 g_free(encoding_name); | |
553 return new_codec; | |
554 } | |
555 | |
556 static PurpleMediaCodec * | |
557 codec_from_fs(const FsCodec *codec) | |
558 { | |
559 PurpleMediaCodec *new_codec; | |
560 GList *iter; | |
561 | |
562 if (codec == NULL) | |
563 return NULL; | |
564 | |
565 new_codec = purple_media_codec_new(codec->id, codec->encoding_name, | |
566 session_type_from_fs(codec->media_type, | |
567 FS_DIRECTION_BOTH), codec->clock_rate); | |
568 g_object_set(new_codec, "channels", codec->channels, NULL); | |
569 | |
570 for (iter = codec->optional_params; iter; iter = g_list_next(iter)) { | |
571 FsCodecParameter *param = (FsCodecParameter*)iter->data; | |
572 purple_media_codec_add_optional_parameter(new_codec, | |
573 param->name, param->value); | |
574 } | |
575 | |
576 return new_codec; | |
577 } | |
578 | |
579 static GList * | |
580 codec_list_from_fs(GList *codecs) | |
581 { | |
582 GList *new_list = NULL; | |
583 | |
584 for (; codecs; codecs = g_list_next(codecs)) { | |
585 new_list = g_list_prepend(new_list, | |
586 codec_from_fs(codecs->data)); | |
587 } | |
588 | |
589 new_list = g_list_reverse(new_list); | |
590 return new_list; | |
591 } | |
592 | |
593 static GList * | |
594 codec_list_to_fs(GList *codecs) | |
595 { | |
596 GList *new_list = NULL; | |
597 | |
598 for (; codecs; codecs = g_list_next(codecs)) { | |
599 new_list = g_list_prepend(new_list, | |
600 codec_to_fs(codecs->data)); | |
601 } | |
602 | |
603 new_list = g_list_reverse(new_list); | |
604 return new_list; | |
605 } | |
606 | |
607 static PurpleMediaBackendFs2Session * | |
608 get_session(PurpleMediaBackendFs2 *self, const gchar *sess_id) | |
609 { | |
610 PurpleMediaBackendFs2Private *priv; | |
611 PurpleMediaBackendFs2Session *session = NULL; | |
612 | |
613 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); | |
614 | |
615 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
616 | |
617 if (priv->sessions != NULL) | |
618 session = g_hash_table_lookup(priv->sessions, sess_id); | |
619 | |
620 return session; | |
621 } | |
622 | |
623 static FsParticipant * | |
624 get_participant(PurpleMediaBackendFs2 *self, const gchar *name) | |
625 { | |
626 PurpleMediaBackendFs2Private *priv; | |
627 FsParticipant *participant = NULL; | |
628 | |
629 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); | |
630 | |
631 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
632 | |
633 if (priv->participants != NULL) | |
634 participant = g_hash_table_lookup(priv->participants, name); | |
635 | |
636 return participant; | |
637 } | |
638 | |
639 static PurpleMediaBackendFs2Stream * | |
640 get_stream(PurpleMediaBackendFs2 *self, | |
641 const gchar *sess_id, const gchar *name) | |
642 { | |
643 PurpleMediaBackendFs2Private *priv; | |
644 GList *streams; | |
645 | |
646 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); | |
647 | |
648 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
649 streams = priv->streams; | |
650 | |
651 for (; streams; streams = g_list_next(streams)) { | |
652 PurpleMediaBackendFs2Stream *stream = streams->data; | |
653 if (!strcmp(stream->session->id, sess_id) && | |
654 !strcmp(stream->participant, name)) | |
655 return stream; | |
656 } | |
657 | |
658 return NULL; | |
659 } | |
660 | |
661 static GList * | |
662 get_streams(PurpleMediaBackendFs2 *self, | |
663 const gchar *sess_id, const gchar *name) | |
664 { | |
665 PurpleMediaBackendFs2Private *priv; | |
666 GList *streams, *ret = NULL; | |
667 | |
668 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); | |
669 | |
670 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
671 streams = priv->streams; | |
672 | |
673 for (; streams; streams = g_list_next(streams)) { | |
674 PurpleMediaBackendFs2Stream *stream = streams->data; | |
675 | |
676 if (sess_id != NULL && strcmp(stream->session->id, sess_id)) | |
677 continue; | |
678 else if (name != NULL && strcmp(stream->participant, name)) | |
679 continue; | |
680 else | |
681 ret = g_list_prepend(ret, stream); | |
682 } | |
683 | |
684 ret = g_list_reverse(ret); | |
685 return ret; | |
686 } | |
687 | |
688 static PurpleMediaBackendFs2Session * | |
689 get_session_from_fs_stream(PurpleMediaBackendFs2 *self, FsStream *stream) | |
690 { | |
691 PurpleMediaBackendFs2Private *priv = | |
692 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
693 FsSession *fssession; | |
694 GList *values; | |
695 | |
696 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); | |
697 g_return_val_if_fail(FS_IS_STREAM(stream), NULL); | |
698 | |
699 g_object_get(stream, "session", &fssession, NULL); | |
700 | |
701 values = g_hash_table_get_values(priv->sessions); | |
702 | |
703 for (; values; values = g_list_delete_link(values, values)) { | |
704 PurpleMediaBackendFs2Session *session = values->data; | |
705 | |
706 if (session->session == fssession) { | |
707 g_list_free(values); | |
708 g_object_unref(fssession); | |
709 return session; | |
710 } | |
711 } | |
712 | |
713 g_object_unref(fssession); | |
714 return NULL; | |
715 } | |
716 | |
717 static void | |
718 gst_handle_message_element(GstBus *bus, GstMessage *msg, | |
719 PurpleMediaBackendFs2 *self) | |
720 { | |
721 PurpleMediaBackendFs2Private *priv = | |
722 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
723 GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); | |
724 static guint level_id = 0; | |
725 | |
726 if (level_id == 0) | |
727 level_id = g_signal_lookup("level", PURPLE_TYPE_MEDIA); | |
728 | |
729 if (g_signal_has_handler_pending(priv->media, level_id, 0, FALSE) | |
730 && gst_structure_has_name( | |
731 gst_message_get_structure(msg), "level")) { | |
732 GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); | |
733 gchar *name; | |
734 gchar *participant = NULL; | |
735 PurpleMediaBackendFs2Session *session = NULL; | |
736 gdouble rms_db; | |
737 gdouble percent; | |
738 const GValue *list; | |
739 const GValue *value; | |
740 | |
741 if (!PURPLE_IS_MEDIA(priv->media) || | |
742 GST_ELEMENT_PARENT(src) != priv->confbin) | |
743 return; | |
744 | |
745 name = gst_element_get_name(src); | |
746 | |
747 if (!strncmp(name, "sendlevel_", 10)) { | |
748 session = get_session(self, name+10); | |
749 } else { | |
750 GList *iter = priv->streams; | |
751 PurpleMediaBackendFs2Stream *stream; | |
752 for (; iter; iter = g_list_next(iter)) { | |
753 stream = iter->data; | |
754 if (stream->level == src) { | |
755 session = stream->session; | |
756 participant = stream->participant; | |
757 break; | |
758 } | |
759 } | |
760 } | |
761 | |
762 g_free(name); | |
763 | |
764 if (!session) | |
765 return; | |
766 | |
767 list = gst_structure_get_value( | |
768 gst_message_get_structure(msg), "rms"); | |
769 value = gst_value_list_get_value(list, 0); | |
770 rms_db = g_value_get_double(value); | |
771 percent = pow(10, rms_db / 20) * 5; | |
772 | |
773 if(percent > 1.0) | |
774 percent = 1.0; | |
775 | |
776 g_signal_emit(priv->media, level_id, 0, | |
777 session->id, participant, percent); | |
778 return; | |
779 } | |
780 | |
781 if (!FS_IS_CONFERENCE(src) || !PURPLE_IS_MEDIA_BACKEND(self) || | |
782 priv->conference != FS_CONFERENCE(src)) | |
783 return; | |
784 | |
785 if (gst_structure_has_name(msg->structure, "farsight-error")) { | |
786 FsError error_no; | |
787 gst_structure_get_enum(msg->structure, "error-no", | |
788 FS_TYPE_ERROR, (gint*)&error_no); | |
789 switch (error_no) { | |
790 case FS_ERROR_NO_CODECS: | |
791 purple_media_error(priv->media, _("No codecs" | |
792 " found. Install some" | |
793 " GStreamer codecs found" | |
794 " in GStreamer plugins" | |
795 " packages.")); | |
796 purple_media_end(priv->media, NULL, NULL); | |
797 break; | |
798 case FS_ERROR_NO_CODECS_LEFT: | |
799 purple_media_error(priv->media, _("No codecs" | |
800 " left. Your codec" | |
801 " preferences in" | |
802 " fs-codecs.conf are too" | |
803 " strict.")); | |
804 purple_media_end(priv->media, NULL, NULL); | |
805 break; | |
806 case FS_ERROR_UNKNOWN_CNAME: | |
807 /* | |
808 * Unknown CName is only a problem for the | |
809 * multicast transmitter which isn't used. | |
810 * It is also deprecated. | |
811 */ | |
812 break; | |
813 default: | |
814 purple_debug_error("backend-fs2", | |
815 "farsight-error: %i: %s\n", | |
816 error_no, | |
817 gst_structure_get_string( | |
818 msg->structure, "error-msg")); | |
819 break; | |
820 } | |
821 | |
822 if (FS_ERROR_IS_FATAL(error_no)) { | |
823 purple_media_error(priv->media, _("A non-recoverable " | |
824 "Farsight2 error has occurred.")); | |
825 purple_media_end(priv->media, NULL, NULL); | |
826 } | |
827 } else if (gst_structure_has_name(msg->structure, | |
828 "farsight-new-local-candidate")) { | |
829 const GValue *value; | |
830 FsStream *stream; | |
831 FsCandidate *local_candidate; | |
832 PurpleMediaCandidate *candidate; | |
833 FsParticipant *participant; | |
834 PurpleMediaBackendFs2Session *session; | |
835 PurpleMediaBackendFs2Stream *media_stream; | |
836 gchar *name; | |
837 | |
838 value = gst_structure_get_value(msg->structure, "stream"); | |
839 stream = g_value_get_object(value); | |
840 value = gst_structure_get_value(msg->structure, "candidate"); | |
841 local_candidate = g_value_get_boxed(value); | |
842 | |
843 session = get_session_from_fs_stream(self, stream); | |
844 | |
845 purple_debug_info("backend-fs2", | |
846 "got new local candidate: %s\n", | |
847 local_candidate->foundation); | |
848 | |
849 g_object_get(stream, "participant", &participant, NULL); | |
850 g_object_get(participant, "cname", &name, NULL); | |
851 g_object_unref(participant); | |
852 | |
853 media_stream = get_stream(self, session->id, name); | |
854 media_stream->local_candidates = g_list_append( | |
855 media_stream->local_candidates, | |
856 fs_candidate_copy(local_candidate)); | |
857 | |
858 candidate = candidate_from_fs(local_candidate); | |
859 g_signal_emit_by_name(self, "new-candidate", | |
860 session->id, name, candidate); | |
861 g_object_unref(candidate); | |
862 } else if (gst_structure_has_name(msg->structure, | |
863 "farsight-local-candidates-prepared")) { | |
864 const GValue *value; | |
865 FsStream *stream; | |
866 FsParticipant *participant; | |
867 PurpleMediaBackendFs2Session *session; | |
868 gchar *name; | |
869 | |
870 value = gst_structure_get_value(msg->structure, "stream"); | |
871 stream = g_value_get_object(value); | |
872 session = get_session_from_fs_stream(self, stream); | |
873 | |
874 g_object_get(stream, "participant", &participant, NULL); | |
875 g_object_get(participant, "cname", &name, NULL); | |
876 g_object_unref(participant); | |
877 | |
878 g_signal_emit_by_name(self, "candidates-prepared", | |
879 session->id, name); | |
880 } else if (gst_structure_has_name(msg->structure, | |
881 "farsight-new-active-candidate-pair")) { | |
882 const GValue *value; | |
883 FsStream *stream; | |
884 FsCandidate *local_candidate; | |
885 FsCandidate *remote_candidate; | |
886 FsParticipant *participant; | |
887 PurpleMediaBackendFs2Session *session; | |
888 PurpleMediaCandidate *lcandidate, *rcandidate; | |
889 gchar *name; | |
890 | |
891 value = gst_structure_get_value(msg->structure, "stream"); | |
892 stream = g_value_get_object(value); | |
893 value = gst_structure_get_value(msg->structure, | |
894 "local-candidate"); | |
895 local_candidate = g_value_get_boxed(value); | |
896 value = gst_structure_get_value(msg->structure, | |
897 "remote-candidate"); | |
898 remote_candidate = g_value_get_boxed(value); | |
899 | |
900 g_object_get(stream, "participant", &participant, NULL); | |
901 g_object_get(participant, "cname", &name, NULL); | |
902 g_object_unref(participant); | |
903 | |
904 session = get_session_from_fs_stream(self, stream); | |
905 | |
906 lcandidate = candidate_from_fs(local_candidate); | |
907 rcandidate = candidate_from_fs(remote_candidate); | |
908 | |
909 g_signal_emit_by_name(self, "active-candidate-pair", | |
910 session->id, name, lcandidate, rcandidate); | |
911 | |
912 g_object_unref(lcandidate); | |
913 g_object_unref(rcandidate); | |
914 } else if (gst_structure_has_name(msg->structure, | |
915 "farsight-recv-codecs-changed")) { | |
916 const GValue *value; | |
917 GList *codecs; | |
918 FsCodec *codec; | |
919 | |
920 value = gst_structure_get_value(msg->structure, "codecs"); | |
921 codecs = g_value_get_boxed(value); | |
922 codec = codecs->data; | |
923 | |
924 purple_debug_info("backend-fs2", | |
925 "farsight-recv-codecs-changed: %s\n", | |
926 codec->encoding_name); | |
927 } else if (gst_structure_has_name(msg->structure, | |
928 "farsight-component-state-changed")) { | |
929 const GValue *value; | |
930 FsStreamState fsstate; | |
931 guint component; | |
932 const gchar *state; | |
933 | |
934 value = gst_structure_get_value(msg->structure, "state"); | |
935 fsstate = g_value_get_enum(value); | |
936 value = gst_structure_get_value(msg->structure, "component"); | |
937 component = g_value_get_uint(value); | |
938 | |
939 switch (fsstate) { | |
940 case FS_STREAM_STATE_FAILED: | |
941 state = "FAILED"; | |
942 break; | |
943 case FS_STREAM_STATE_DISCONNECTED: | |
944 state = "DISCONNECTED"; | |
945 break; | |
946 case FS_STREAM_STATE_GATHERING: | |
947 state = "GATHERING"; | |
948 break; | |
949 case FS_STREAM_STATE_CONNECTING: | |
950 state = "CONNECTING"; | |
951 break; | |
952 case FS_STREAM_STATE_CONNECTED: | |
953 state = "CONNECTED"; | |
954 break; | |
955 case FS_STREAM_STATE_READY: | |
956 state = "READY"; | |
957 break; | |
958 default: | |
959 state = "UNKNOWN"; | |
960 break; | |
961 } | |
962 | |
963 purple_debug_info("backend-fs2", | |
964 "farsight-component-state-changed: " | |
965 "component: %u state: %s\n", | |
966 component, state); | |
967 } else if (gst_structure_has_name(msg->structure, | |
968 "farsight-send-codec-changed")) { | |
969 const GValue *value; | |
970 FsCodec *codec; | |
971 gchar *codec_str; | |
972 | |
973 value = gst_structure_get_value(msg->structure, "codec"); | |
974 codec = g_value_get_boxed(value); | |
975 codec_str = fs_codec_to_string(codec); | |
976 | |
977 purple_debug_info("backend-fs2", | |
978 "farsight-send-codec-changed: codec: %s\n", | |
979 codec_str); | |
980 | |
981 g_free(codec_str); | |
982 } else if (gst_structure_has_name(msg->structure, | |
983 "farsight-codecs-changed")) { | |
984 const GValue *value; | |
985 FsSession *fssession; | |
986 GList *sessions; | |
987 | |
988 value = gst_structure_get_value(msg->structure, "session"); | |
989 fssession = g_value_get_object(value); | |
990 sessions = g_hash_table_get_values(priv->sessions); | |
991 | |
992 for (; sessions; sessions = | |
993 g_list_delete_link(sessions, sessions)) { | |
994 PurpleMediaBackendFs2Session *session = sessions->data; | |
995 gchar *session_id; | |
996 | |
997 if (session->session != fssession) | |
998 continue; | |
999 | |
1000 session_id = g_strdup(session->id); | |
1001 g_signal_emit_by_name(self, "codecs-changed", | |
1002 session_id); | |
1003 g_free(session_id); | |
1004 g_list_free(sessions); | |
1005 break; | |
1006 } | |
1007 } | |
1008 } | |
1009 | |
1010 static void | |
1011 gst_handle_message_error(GstBus *bus, GstMessage *msg, | |
1012 PurpleMediaBackendFs2 *self) | |
1013 { | |
1014 PurpleMediaBackendFs2Private *priv = | |
1015 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
1016 GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(msg)); | |
1017 GstElement *lastElement = NULL; | |
1018 GList *sessions; | |
1019 | |
1020 while (!GST_IS_PIPELINE(element)) { | |
1021 if (element == priv->confbin) | |
1022 break; | |
1023 | |
1024 lastElement = element; | |
1025 element = GST_ELEMENT_PARENT(element); | |
1026 } | |
1027 | |
1028 if (!GST_IS_PIPELINE(element)) | |
1029 return; | |
1030 | |
1031 sessions = purple_media_get_session_ids(priv->media); | |
1032 | |
1033 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { | |
1034 if (purple_media_get_src(priv->media, sessions->data) | |
1035 != lastElement) | |
1036 continue; | |
1037 | |
1038 if (purple_media_get_session_type(priv->media, sessions->data) | |
1039 & PURPLE_MEDIA_AUDIO) | |
1040 purple_media_error(priv->media, | |
1041 _("Error with your microphone")); | |
1042 else | |
1043 purple_media_error(priv->media, | |
1044 _("Error with your webcam")); | |
1045 | |
1046 break; | |
1047 } | |
1048 | |
1049 g_list_free(sessions); | |
1050 | |
1051 purple_media_error(priv->media, _("Conference error")); | |
1052 purple_media_end(priv->media, NULL, NULL); | |
1053 } | |
1054 | |
1055 static gboolean | |
1056 gst_bus_cb(GstBus *bus, GstMessage *msg, PurpleMediaBackendFs2 *self) | |
1057 { | |
1058 switch(GST_MESSAGE_TYPE(msg)) { | |
1059 case GST_MESSAGE_ELEMENT: | |
1060 gst_handle_message_element(bus, msg, self); | |
1061 break; | |
1062 case GST_MESSAGE_ERROR: | |
1063 gst_handle_message_error(bus, msg, self); | |
1064 break; | |
1065 default: | |
1066 break; | |
1067 } | |
1068 | |
1069 return TRUE; | |
1070 } | |
1071 | |
1072 static void | |
1073 state_changed_cb(PurpleMedia *media, PurpleMediaState state, | |
1074 gchar *sid, gchar *name, PurpleMediaBackendFs2 *self) | |
1075 { | |
1076 } | |
1077 | |
1078 static void | |
1079 stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type, | |
1080 gchar *sid, gchar *name, gboolean local, | |
1081 PurpleMediaBackendFs2 *self) | |
1082 { | |
1083 if (type == PURPLE_MEDIA_INFO_ACCEPT && sid != NULL && name != NULL) { | |
1084 PurpleMediaBackendFs2Stream *stream = | |
1085 get_stream(self, sid, name); | |
1086 GError *err = NULL; | |
1087 | |
1088 g_object_set(G_OBJECT(stream->stream), "direction", | |
1089 session_type_to_fs_stream_direction( | |
1090 stream->session->type), NULL); | |
1091 | |
1092 if (stream->remote_candidates == NULL || | |
1093 purple_media_is_initiator(media, sid, name)) | |
1094 return; | |
1095 | |
1096 fs_stream_set_remote_candidates(stream->stream, | |
1097 stream->remote_candidates, &err); | |
1098 | |
1099 if (err == NULL) | |
1100 return; | |
1101 | |
1102 purple_debug_error("backend-fs2", "Error adding " | |
1103 "remote candidates: %s\n", | |
1104 err->message); | |
1105 g_error_free(err); | |
1106 } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_MUTE || | |
1107 type == PURPLE_MEDIA_INFO_UNMUTE)) { | |
1108 PurpleMediaBackendFs2Private *priv = | |
1109 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
1110 gboolean active = (type == PURPLE_MEDIA_INFO_MUTE); | |
1111 GList *sessions; | |
1112 | |
1113 if (sid == NULL) | |
1114 sessions = g_hash_table_get_values(priv->sessions); | |
1115 else | |
1116 sessions = g_list_prepend(NULL, | |
1117 get_session(self, sid)); | |
1118 | |
1119 purple_debug_info("media", "Turning mute %s\n", | |
1120 active ? "on" : "off"); | |
1121 | |
1122 for (; sessions; sessions = g_list_delete_link( | |
1123 sessions, sessions)) { | |
1124 PurpleMediaBackendFs2Session *session = | |
1125 sessions->data; | |
1126 | |
1127 if (session->type & PURPLE_MEDIA_SEND_AUDIO) { | |
1128 gchar *name = g_strdup_printf("volume_%s", | |
1129 session->id); | |
1130 GstElement *volume = gst_bin_get_by_name( | |
1131 GST_BIN(priv->confbin), name); | |
1132 g_free(name); | |
1133 g_object_set(volume, "mute", active, NULL); | |
1134 } | |
1135 } | |
1136 } else if (local == TRUE && (type == PURPLE_MEDIA_INFO_PAUSE || | |
1137 type == PURPLE_MEDIA_INFO_UNPAUSE)) { | |
1138 gboolean active = (type == PURPLE_MEDIA_INFO_PAUSE); | |
1139 GList *streams = get_streams(self, sid, name); | |
1140 for (; streams; streams = | |
1141 g_list_delete_link(streams, streams)) { | |
1142 PurpleMediaBackendFs2Stream *stream = streams->data; | |
1143 if (stream->session->type & PURPLE_MEDIA_SEND_VIDEO) { | |
1144 g_object_set(stream->stream, "direction", | |
1145 session_type_to_fs_stream_direction( | |
1146 stream->session->type & ((active) ? | |
1147 ~PURPLE_MEDIA_SEND_VIDEO : | |
1148 PURPLE_MEDIA_VIDEO)), NULL); | |
1149 } | |
1150 } | |
1151 } | |
1152 } | |
1153 | |
1154 static gboolean | |
1155 init_conference(PurpleMediaBackendFs2 *self) | |
1156 { | |
1157 PurpleMediaBackendFs2Private *priv = | |
1158 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
1159 GstElement *pipeline; | |
1160 GstBus *bus; | |
1161 gchar *name; | |
1162 | |
1163 priv->conference = FS_CONFERENCE( | |
1164 gst_element_factory_make(priv->conference_type, NULL)); | |
1165 | |
1166 if (priv->conference == NULL) { | |
1167 purple_debug_error("backend-fs2", "Conference == NULL\n"); | |
1168 return FALSE; | |
1169 } | |
1170 | |
1171 pipeline = purple_media_manager_get_pipeline( | |
1172 purple_media_get_manager(priv->media)); | |
1173 | |
1174 if (pipeline == NULL) { | |
1175 purple_debug_error("backend-fs2", | |
1176 "Couldn't retrieve pipeline.\n"); | |
1177 return FALSE; | |
1178 } | |
1179 | |
1180 name = g_strdup_printf("conf_%p", priv->conference); | |
1181 priv->confbin = gst_bin_new(name); | |
1182 if (priv->confbin == NULL) { | |
1183 purple_debug_error("backend-fs2", | |
1184 "Couldn't create confbin.\n"); | |
1185 return FALSE; | |
1186 } | |
1187 | |
1188 g_free(name); | |
1189 | |
1190 bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); | |
1191 if (bus == NULL) { | |
1192 purple_debug_error("backend-fs2", | |
1193 "Couldn't get the pipeline's bus.\n"); | |
1194 return FALSE; | |
1195 } | |
1196 | |
1197 g_signal_connect(G_OBJECT(bus), "message", | |
1198 G_CALLBACK(gst_bus_cb), self); | |
1199 gst_object_unref(bus); | |
1200 | |
1201 if (!gst_bin_add(GST_BIN(pipeline), | |
1202 GST_ELEMENT(priv->confbin))) { | |
1203 purple_debug_error("backend-fs2", "Couldn't add confbin " | |
1204 "element to the pipeline\n"); | |
1205 return FALSE; | |
1206 } | |
1207 | |
1208 if (!gst_bin_add(GST_BIN(priv->confbin), | |
1209 GST_ELEMENT(priv->conference))) { | |
1210 purple_debug_error("backend-fs2", "Couldn't add conference " | |
1211 "element to the confbin\n"); | |
1212 return FALSE; | |
1213 } | |
1214 | |
1215 if (gst_element_set_state(GST_ELEMENT(priv->confbin), | |
1216 GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { | |
1217 purple_debug_error("backend-fs2", | |
1218 "Failed to start conference.\n"); | |
1219 return FALSE; | |
1220 } | |
1221 | |
1222 return TRUE; | |
1223 } | |
1224 | |
1225 static void | |
1226 gst_element_added_cb(FsElementAddedNotifier *self, | |
1227 GstBin *bin, GstElement *element, gpointer user_data) | |
1228 { | |
1229 /* | |
1230 * Hack to make H264 work with Gmail video. | |
1231 */ | |
1232 if (!strncmp(GST_ELEMENT_NAME(element), "x264", 4)) { | |
1233 g_object_set(GST_OBJECT(element), "cabac", FALSE, NULL); | |
1234 } | |
1235 } | |
1236 | |
1237 static gboolean | |
1238 create_src(PurpleMediaBackendFs2 *self, const gchar *sess_id, | |
1239 PurpleMediaSessionType type) | |
1240 { | |
1241 PurpleMediaBackendFs2Private *priv = | |
1242 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
1243 PurpleMediaBackendFs2Session *session; | |
1244 PurpleMediaSessionType session_type; | |
1245 FsMediaType media_type = session_type_to_fs_media_type(type); | |
1246 FsStreamDirection type_direction = | |
1247 session_type_to_fs_stream_direction(type); | |
1248 GstElement *src; | |
1249 GstPad *sinkpad, *srcpad; | |
1250 | |
1251 if ((type_direction & FS_DIRECTION_SEND) == 0) | |
1252 return TRUE; | |
1253 | |
1254 session_type = session_type_from_fs( | |
1255 media_type, FS_DIRECTION_SEND); | |
1256 src = purple_media_manager_get_element( | |
1257 purple_media_get_manager(priv->media), | |
1258 session_type, priv->media, sess_id, NULL); | |
1259 | |
1260 if (!GST_IS_ELEMENT(src)) { | |
1261 purple_debug_error("backend-fs2", | |
1262 "Error creating src for session %s\n", | |
1263 sess_id); | |
1264 return FALSE; | |
1265 } | |
1266 | |
1267 session = get_session(self, sess_id); | |
1268 | |
1269 if (session == NULL) { | |
1270 purple_debug_warning("backend-fs2", | |
1271 "purple_media_set_src: trying to set" | |
1272 " src on non-existent session\n"); | |
1273 return FALSE; | |
1274 } | |
1275 | |
1276 if (session->src) | |
1277 gst_object_unref(session->src); | |
1278 | |
1279 session->src = src; | |
1280 gst_element_set_locked_state(session->src, TRUE); | |
1281 | |
1282 session->tee = gst_element_factory_make("tee", NULL); | |
1283 gst_bin_add(GST_BIN(priv->confbin), session->tee); | |
1284 | |
1285 /* This supposedly isn't necessary, but it silences some warnings */ | |
1286 if (GST_ELEMENT_PARENT(priv->confbin) | |
1287 == GST_ELEMENT_PARENT(session->src)) { | |
1288 GstPad *pad = gst_element_get_static_pad(session->tee, "sink"); | |
1289 GstPad *ghost = gst_ghost_pad_new(NULL, pad); | |
1290 gst_object_unref(pad); | |
1291 gst_pad_set_active(ghost, TRUE); | |
1292 gst_element_add_pad(priv->confbin, ghost); | |
1293 } | |
1294 | |
1295 gst_element_set_state(session->tee, GST_STATE_PLAYING); | |
1296 gst_element_link(session->src, priv->confbin); | |
1297 | |
1298 g_object_get(session->session, "sink-pad", &sinkpad, NULL); | |
1299 if (session->type & PURPLE_MEDIA_SEND_AUDIO) { | |
1300 gchar *name = g_strdup_printf("volume_%s", session->id); | |
1301 GstElement *level; | |
1302 GstElement *volume = gst_element_factory_make("volume", name); | |
1303 double input_volume = purple_prefs_get_int( | |
1304 "/purple/media/audio/volume/input")/10.0; | |
1305 g_free(name); | |
1306 name = g_strdup_printf("sendlevel_%s", session->id); | |
1307 level = gst_element_factory_make("level", name); | |
1308 g_free(name); | |
1309 gst_bin_add(GST_BIN(priv->confbin), volume); | |
1310 gst_bin_add(GST_BIN(priv->confbin), level); | |
1311 gst_element_set_state(level, GST_STATE_PLAYING); | |
1312 gst_element_set_state(volume, GST_STATE_PLAYING); | |
1313 gst_element_link(volume, level); | |
1314 gst_element_link(session->tee, volume); | |
1315 srcpad = gst_element_get_static_pad(level, "src"); | |
1316 g_object_set(volume, "volume", input_volume, NULL); | |
1317 } else { | |
1318 srcpad = gst_element_get_request_pad(session->tee, "src%d"); | |
1319 } | |
1320 | |
1321 purple_debug_info("backend-fs2", "connecting pad: %s\n", | |
1322 gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK | |
1323 ? "success" : "failure"); | |
1324 gst_element_set_locked_state(session->src, FALSE); | |
1325 gst_object_unref(session->src); | |
1326 | |
1327 gst_element_set_state(session->src, GST_STATE_PLAYING); | |
1328 | |
1329 purple_media_manager_create_output_window(purple_media_get_manager( | |
1330 priv->media), priv->media, sess_id, NULL); | |
1331 | |
1332 return TRUE; | |
1333 } | |
1334 | |
1335 static gboolean | |
1336 create_session(PurpleMediaBackendFs2 *self, const gchar *sess_id, | |
1337 PurpleMediaSessionType type, gboolean initiator, | |
1338 const gchar *transmitter) | |
1339 { | |
1340 PurpleMediaBackendFs2Private *priv = | |
1341 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
1342 PurpleMediaBackendFs2Session *session; | |
1343 GError *err = NULL; | |
1344 GList *codec_conf = NULL, *iter = NULL; | |
1345 gchar *filename = NULL; | |
1346 gboolean is_nice = !strcmp(transmitter, "nice"); | |
1347 | |
1348 session = g_new0(PurpleMediaBackendFs2Session, 1); | |
1349 | |
1350 session->session = fs_conference_new_session(priv->conference, | |
1351 session_type_to_fs_media_type(type), &err); | |
1352 | |
1353 if (err != NULL) { | |
1354 purple_media_error(priv->media, | |
1355 _("Error creating session: %s"), | |
1356 err->message); | |
1357 g_error_free(err); | |
1358 g_free(session); | |
1359 return FALSE; | |
1360 } | |
1361 | |
1362 filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL); | |
1363 codec_conf = fs_codec_list_from_keyfile(filename, &err); | |
1364 g_free(filename); | |
1365 | |
1366 if (err != NULL) { | |
1367 if (err->code == 4) | |
1368 purple_debug_info("backend-fs2", "Couldn't read " | |
1369 "fs-codec.conf: %s\n", | |
1370 err->message); | |
1371 else | |
1372 purple_debug_error("backend-fs2", "Error reading " | |
1373 "fs-codec.conf: %s\n", | |
1374 err->message); | |
1375 g_error_free(err); | |
1376 } | |
1377 | |
1378 /* | |
1379 * Add SPEEX if the configuration file doesn't exist or | |
1380 * there isn't a speex entry. | |
1381 */ | |
1382 for (iter = codec_conf; iter; iter = g_list_next(iter)) { | |
1383 FsCodec *codec = iter->data; | |
1384 if (!g_ascii_strcasecmp(codec->encoding_name, "speex")) | |
1385 break; | |
1386 } | |
1387 | |
1388 if (iter == NULL) { | |
1389 codec_conf = g_list_prepend(codec_conf, | |
1390 fs_codec_new(FS_CODEC_ID_ANY, | |
1391 "SPEEX", FS_MEDIA_TYPE_AUDIO, 8000)); | |
1392 codec_conf = g_list_prepend(codec_conf, | |
1393 fs_codec_new(FS_CODEC_ID_ANY, | |
1394 "SPEEX", FS_MEDIA_TYPE_AUDIO, 16000)); | |
1395 } | |
1396 | |
1397 fs_session_set_codec_preferences(session->session, codec_conf, NULL); | |
1398 fs_codec_list_destroy(codec_conf); | |
1399 | |
1400 /* | |
1401 * Removes a 5-7 second delay before | |
1402 * receiving the src-pad-added signal. | |
1403 * Only works for non-multicast FsRtpSessions. | |
1404 */ | |
1405 if (is_nice || !strcmp(transmitter, "rawudp")) | |
1406 g_object_set(G_OBJECT(session->session), | |
1407 "no-rtcp-timeout", 0, NULL); | |
1408 | |
1409 /* | |
1410 * Hack to make x264 work with Gmail video. | |
1411 */ | |
1412 if (is_nice && !strcmp(sess_id, "google-video")) { | |
1413 FsElementAddedNotifier *notifier = | |
1414 fs_element_added_notifier_new(); | |
1415 g_signal_connect(G_OBJECT(notifier), "element-added", | |
1416 G_CALLBACK(gst_element_added_cb), NULL); | |
1417 fs_element_added_notifier_add(notifier, | |
1418 GST_BIN(priv->conference)); | |
1419 } | |
1420 | |
1421 session->id = g_strdup(sess_id); | |
1422 session->backend = self; | |
1423 session->type = type; | |
1424 | |
1425 if (!priv->sessions) { | |
1426 purple_debug_info("backend-fs2", | |
1427 "Creating hash table for sessions\n"); | |
1428 priv->sessions = g_hash_table_new(g_str_hash, g_str_equal); | |
1429 } | |
1430 | |
1431 g_hash_table_insert(priv->sessions, g_strdup(session->id), session); | |
1432 | |
1433 if (!create_src(self, sess_id, type)) { | |
1434 purple_debug_info("backend-fs2", "Error creating the src\n"); | |
1435 return FALSE; | |
1436 } | |
1437 | |
1438 return TRUE; | |
1439 } | |
1440 | |
1441 static gboolean | |
1442 create_participant(PurpleMediaBackendFs2 *self, const gchar *name) | |
1443 { | |
1444 PurpleMediaBackendFs2Private *priv = | |
1445 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
1446 FsParticipant *participant; | |
1447 GError *err = NULL; | |
1448 | |
1449 participant = fs_conference_new_participant( | |
1450 priv->conference, name, &err); | |
1451 | |
1452 if (err) { | |
1453 purple_debug_error("backend-fs2", | |
1454 "Error creating participant: %s\n", | |
1455 err->message); | |
1456 g_error_free(err); | |
1457 return FALSE; | |
1458 } | |
1459 | |
1460 if (!priv->participants) { | |
1461 purple_debug_info("backend-fs2", | |
1462 "Creating hash table for participants\n"); | |
1463 priv->participants = g_hash_table_new_full(g_str_hash, | |
1464 g_str_equal, g_free, NULL); | |
1465 } | |
1466 | |
1467 g_hash_table_insert(priv->participants, g_strdup(name), participant); | |
1468 | |
1469 return TRUE; | |
1470 } | |
1471 | |
1472 static gboolean | |
1473 src_pad_added_cb_cb(PurpleMediaBackendFs2Stream *stream) | |
1474 { | |
1475 PurpleMediaBackendFs2Private *priv; | |
1476 | |
1477 g_return_val_if_fail(stream != NULL, FALSE); | |
1478 | |
1479 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(stream->session->backend); | |
1480 stream->connected_cb_id = 0; | |
1481 | |
1482 purple_media_manager_create_output_window( | |
1483 purple_media_get_manager(priv->media), priv->media, | |
1484 stream->session->id, stream->participant); | |
1485 | |
1486 g_signal_emit_by_name(priv->media, "state-changed", | |
1487 PURPLE_MEDIA_STATE_CONNECTED, | |
1488 stream->session->id, stream->participant); | |
1489 return FALSE; | |
1490 } | |
1491 | |
1492 static void | |
1493 src_pad_added_cb(FsStream *fsstream, GstPad *srcpad, | |
1494 FsCodec *codec, PurpleMediaBackendFs2Stream *stream) | |
1495 { | |
1496 PurpleMediaBackendFs2Private *priv; | |
1497 GstPad *sinkpad; | |
1498 | |
1499 g_return_if_fail(FS_IS_STREAM(fsstream)); | |
1500 g_return_if_fail(stream != NULL); | |
1501 | |
1502 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(stream->session->backend); | |
1503 | |
1504 if (stream->src == NULL) { | |
1505 GstElement *sink = NULL; | |
1506 | |
1507 if (codec->media_type == FS_MEDIA_TYPE_AUDIO) { | |
1508 GstElement *queue = NULL; | |
1509 double output_volume = purple_prefs_get_int( | |
1510 "/purple/media/audio/volume/output")/10.0; | |
1511 /* | |
1512 * Should this instead be: | |
1513 * audioconvert ! audioresample ! liveadder ! | |
1514 * audioresample ! audioconvert ! realsink | |
1515 */ | |
1516 queue = gst_element_factory_make("queue", NULL); | |
1517 stream->volume = gst_element_factory_make( | |
1518 "volume", NULL); | |
1519 g_object_set(stream->volume, "volume", | |
1520 output_volume, NULL); | |
1521 stream->level = gst_element_factory_make( | |
1522 "level", NULL); | |
1523 stream->src = gst_element_factory_make( | |
1524 "liveadder", NULL); | |
1525 sink = purple_media_manager_get_element( | |
1526 purple_media_get_manager(priv->media), | |
1527 PURPLE_MEDIA_RECV_AUDIO, priv->media, | |
1528 stream->session->id, | |
1529 stream->participant); | |
1530 gst_bin_add(GST_BIN(priv->confbin), queue); | |
1531 gst_bin_add(GST_BIN(priv->confbin), stream->volume); | |
1532 gst_bin_add(GST_BIN(priv->confbin), stream->level); | |
1533 gst_bin_add(GST_BIN(priv->confbin), sink); | |
1534 gst_element_set_state(sink, GST_STATE_PLAYING); | |
1535 gst_element_set_state(stream->level, GST_STATE_PLAYING); | |
1536 gst_element_set_state(stream->volume, GST_STATE_PLAYING); | |
1537 gst_element_set_state(queue, GST_STATE_PLAYING); | |
1538 gst_element_link(stream->level, sink); | |
1539 gst_element_link(stream->volume, stream->level); | |
1540 gst_element_link(queue, stream->volume); | |
1541 sink = queue; | |
1542 } else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) { | |
1543 stream->src = gst_element_factory_make( | |
1544 "fsfunnel", NULL); | |
1545 sink = gst_element_factory_make( | |
1546 "fakesink", NULL); | |
1547 g_object_set(G_OBJECT(sink), "async", FALSE, NULL); | |
1548 gst_bin_add(GST_BIN(priv->confbin), sink); | |
1549 gst_element_set_state(sink, GST_STATE_PLAYING); | |
1550 } | |
1551 stream->tee = gst_element_factory_make("tee", NULL); | |
1552 gst_bin_add_many(GST_BIN(priv->confbin), | |
1553 stream->src, stream->tee, NULL); | |
1554 gst_element_set_state(stream->tee, GST_STATE_PLAYING); | |
1555 gst_element_set_state(stream->src, GST_STATE_PLAYING); | |
1556 gst_element_link_many(stream->src, stream->tee, sink, NULL); | |
1557 } | |
1558 | |
1559 sinkpad = gst_element_get_request_pad(stream->src, "sink%d"); | |
1560 gst_pad_link(srcpad, sinkpad); | |
1561 gst_object_unref(sinkpad); | |
1562 | |
1563 stream->connected_cb_id = purple_timeout_add(0, | |
1564 (GSourceFunc)src_pad_added_cb_cb, stream); | |
1565 } | |
1566 | |
1567 static gboolean | |
1568 create_stream(PurpleMediaBackendFs2 *self, | |
1569 const gchar *sess_id, const gchar *who, | |
1570 PurpleMediaSessionType type, gboolean initiator, | |
1571 const gchar *transmitter, | |
1572 guint num_params, GParameter *params) | |
1573 { | |
1574 PurpleMediaBackendFs2Private *priv = | |
1575 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
1576 GError *err = NULL; | |
1577 FsStream *fsstream = NULL; | |
1578 const gchar *stun_ip = purple_network_get_stun_ip(); | |
1579 const gchar *turn_ip = purple_network_get_turn_ip(); | |
1580 guint _num_params = num_params; | |
1581 GParameter *_params = g_new0(GParameter, num_params + 3); | |
1582 FsStreamDirection type_direction = | |
1583 session_type_to_fs_stream_direction(type); | |
1584 PurpleMediaBackendFs2Session *session; | |
1585 PurpleMediaBackendFs2Stream *stream; | |
1586 FsParticipant *participant; | |
1587 | |
1588 memcpy(_params, params, sizeof(GParameter) * num_params); | |
1589 | |
1590 /* set the controlling mode parameter */ | |
1591 _params[_num_params].name = "controlling-mode"; | |
1592 g_value_init(&_params[_num_params].value, G_TYPE_BOOLEAN); | |
1593 g_value_set_boolean(&_params[_num_params].value, initiator); | |
1594 ++_num_params; | |
1595 | |
1596 if (stun_ip) { | |
1597 purple_debug_info("backend-fs2", | |
1598 "Setting stun-ip on new stream: %s\n", stun_ip); | |
1599 | |
1600 _params[_num_params].name = "stun-ip"; | |
1601 g_value_init(&_params[_num_params].value, G_TYPE_STRING); | |
1602 g_value_set_string(&_params[_num_params].value, stun_ip); | |
1603 ++_num_params; | |
1604 } | |
1605 | |
1606 if (turn_ip && !strcmp("nice", transmitter)) { | |
1607 GValueArray *relay_info = g_value_array_new(0); | |
1608 GValue value; | |
1609 gint turn_port = purple_prefs_get_int( | |
1610 "/purple/network/turn_port"); | |
1611 const gchar *username = purple_prefs_get_string( | |
1612 "/purple/network/turn_username"); | |
1613 const gchar *password = purple_prefs_get_string( | |
1614 "/purple/network/turn_password"); | |
1615 GstStructure *turn_setup = gst_structure_new("relay-info", | |
1616 "ip", G_TYPE_STRING, turn_ip, | |
1617 "port", G_TYPE_UINT, turn_port, | |
1618 "username", G_TYPE_STRING, username, | |
1619 "password", G_TYPE_STRING, password, | |
1620 NULL); | |
1621 | |
1622 if (!turn_setup) { | |
1623 purple_debug_error("backend-fs2", | |
1624 "Error creating relay info structure"); | |
1625 return FALSE; | |
1626 } | |
1627 | |
1628 memset(&value, 0, sizeof(GValue)); | |
1629 g_value_init(&value, GST_TYPE_STRUCTURE); | |
1630 gst_value_set_structure(&value, turn_setup); | |
1631 relay_info = g_value_array_append(relay_info, &value); | |
1632 gst_structure_free(turn_setup); | |
1633 | |
1634 purple_debug_info("backend-fs2", | |
1635 "Setting relay-info on new stream\n"); | |
1636 _params[_num_params].name = "relay-info"; | |
1637 g_value_init(&_params[_num_params].value, | |
1638 G_TYPE_VALUE_ARRAY); | |
1639 g_value_set_boxed(&_params[_num_params].value, | |
1640 relay_info); | |
1641 g_value_array_free(relay_info); | |
1642 } | |
1643 | |
1644 session = get_session(self, sess_id); | |
1645 | |
1646 if (session == NULL) { | |
1647 purple_debug_error("backend-fs2", | |
1648 "Couldn't find session to create stream.\n"); | |
1649 return FALSE; | |
1650 } | |
1651 | |
1652 participant = get_participant(self, who); | |
1653 | |
1654 if (participant == NULL) { | |
1655 purple_debug_error("backend-fs2", "Couldn't find " | |
1656 "participant to create stream.\n"); | |
1657 return FALSE; | |
1658 } | |
1659 | |
1660 fsstream = fs_session_new_stream(session->session, participant, | |
1661 initiator == TRUE ? type_direction : | |
1662 (type_direction & FS_DIRECTION_RECV), transmitter, | |
1663 _num_params, _params, &err); | |
1664 g_free(_params); | |
1665 | |
1666 if (fsstream == NULL) { | |
1667 if (err) { | |
1668 purple_debug_error("backend-fs2", | |
1669 "Error creating stream: %s\n", | |
1670 err && err->message ? | |
1671 err->message : "NULL"); | |
1672 g_error_free(err); | |
1673 } else | |
1674 purple_debug_error("backend-fs2", | |
1675 "Error creating stream\n"); | |
1676 return FALSE; | |
1677 } | |
1678 | |
1679 stream = g_new0(PurpleMediaBackendFs2Stream, 1); | |
1680 stream->participant = g_strdup(who); | |
1681 stream->session = session; | |
1682 stream->stream = fsstream; | |
1683 | |
1684 priv->streams = g_list_append(priv->streams, stream); | |
1685 | |
1686 g_signal_connect(G_OBJECT(fsstream), "src-pad-added", | |
1687 G_CALLBACK(src_pad_added_cb), stream); | |
1688 | |
1689 return TRUE; | |
1690 } | |
1691 | |
1692 static gboolean | |
1693 purple_media_backend_fs2_add_stream(PurpleMediaBackend *self, | |
1694 const gchar *sess_id, const gchar *who, | |
1695 PurpleMediaSessionType type, gboolean initiator, | |
1696 const gchar *transmitter, | |
1697 guint num_params, GParameter *params) | |
1698 { | |
1699 PurpleMediaBackendFs2 *backend = PURPLE_MEDIA_BACKEND_FS2(self); | |
1700 PurpleMediaBackendFs2Private *priv = | |
1701 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(backend); | |
1702 PurpleMediaBackendFs2Stream *stream; | |
1703 | |
1704 if (priv->conference == NULL && !init_conference(backend)) { | |
1705 purple_debug_error("backend-fs2", | |
1706 "Error initializing the conference.\n"); | |
1707 return FALSE; | |
1708 } | |
1709 | |
1710 if (get_session(backend, sess_id) == NULL && | |
1711 !create_session(backend, sess_id, type, | |
1712 initiator, transmitter)) { | |
1713 purple_debug_error("backend-fs2", | |
1714 "Error creating the session.\n"); | |
1715 return FALSE; | |
1716 } | |
1717 | |
1718 if (get_participant(backend, who) == NULL && | |
1719 !create_participant(backend, who)) { | |
1720 purple_debug_error("backend-fs2", | |
1721 "Error creating the participant.\n"); | |
1722 return FALSE; | |
1723 } | |
1724 | |
1725 stream = get_stream(backend, sess_id, who); | |
1726 | |
1727 if (stream != NULL) { | |
1728 FsStreamDirection type_direction = | |
1729 session_type_to_fs_stream_direction(type); | |
1730 | |
1731 if (session_type_to_fs_stream_direction( | |
1732 stream->session->type) != type_direction) { | |
1733 /* change direction */ | |
1734 g_object_set(stream->stream, "direction", | |
1735 type_direction, NULL); | |
1736 } | |
1737 } else if (!create_stream(backend, sess_id, who, type, | |
1738 initiator, transmitter, num_params, params)) { | |
1739 purple_debug_error("backend-fs2", | |
1740 "Error creating the stream.\n"); | |
1741 return FALSE; | |
1742 } | |
1743 | |
1744 return TRUE; | |
1745 } | |
1746 | |
1747 static void | |
1748 purple_media_backend_fs2_add_remote_candidates(PurpleMediaBackend *self, | |
1749 const gchar *sess_id, const gchar *participant, | |
1750 GList *remote_candidates) | |
1751 { | |
1752 PurpleMediaBackendFs2Private *priv; | |
1753 PurpleMediaBackendFs2Stream *stream; | |
1754 GError *err = NULL; | |
1755 | |
1756 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self)); | |
1757 | |
1758 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
1759 stream = get_stream(PURPLE_MEDIA_BACKEND_FS2(self), | |
1760 sess_id, participant); | |
1761 | |
1762 if (stream == NULL) { | |
1763 purple_debug_error("backend-fs2", | |
1764 "purple_media_add_remote_candidates: " | |
1765 "couldn't find stream %s %s.\n", | |
1766 sess_id ? sess_id : "(null)", | |
1767 participant ? participant : "(null)"); | |
1768 return; | |
1769 } | |
1770 | |
1771 stream->remote_candidates = g_list_concat(stream->remote_candidates, | |
1772 candidate_list_to_fs(remote_candidates)); | |
1773 | |
1774 if (purple_media_is_initiator(priv->media, sess_id, participant) || | |
1775 purple_media_accepted( | |
1776 priv->media, sess_id, participant)) { | |
1777 fs_stream_set_remote_candidates(stream->stream, | |
1778 stream->remote_candidates, &err); | |
1779 | |
1780 if (err) { | |
1781 purple_debug_error("backend-fs2", "Error adding remote" | |
1782 " candidates: %s\n", err->message); | |
1783 g_error_free(err); | |
1784 } | |
1785 } | |
1786 } | |
1787 | |
1788 static gboolean | |
1789 purple_media_backend_fs2_codecs_ready(PurpleMediaBackend *self, | |
1790 const gchar *sess_id) | |
1791 { | |
1792 PurpleMediaBackendFs2Private *priv; | |
1793 gboolean ret; | |
1794 | |
1795 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE); | |
1796 | |
1797 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
1798 | |
1799 if (sess_id != NULL) { | |
1800 PurpleMediaBackendFs2Session *session = get_session( | |
1801 PURPLE_MEDIA_BACKEND_FS2(self), sess_id); | |
1802 | |
1803 if (session == NULL) | |
1804 return FALSE; | |
1805 | |
1806 if (session->type & (PURPLE_MEDIA_SEND_AUDIO | | |
1807 PURPLE_MEDIA_SEND_VIDEO)) | |
1808 g_object_get(session->session, | |
1809 "codecs-ready", &ret, NULL); | |
1810 else | |
1811 ret = TRUE; | |
1812 } else { | |
1813 GList *values = g_hash_table_get_values(priv->sessions); | |
1814 | |
1815 for (; values; values = g_list_delete_link(values, values)) { | |
1816 PurpleMediaBackendFs2Session *session = values->data; | |
1817 if (session->type & (PURPLE_MEDIA_SEND_AUDIO | | |
1818 PURPLE_MEDIA_SEND_VIDEO)) | |
1819 g_object_get(session->session, | |
1820 "codecs-ready", &ret, NULL); | |
1821 else | |
1822 ret = TRUE; | |
1823 | |
1824 if (ret == FALSE) | |
1825 break; | |
1826 } | |
1827 | |
1828 if (values != NULL) | |
1829 g_list_free(values); | |
1830 } | |
1831 | |
1832 return ret; | |
1833 } | |
1834 | |
1835 static GList * | |
1836 purple_media_backend_fs2_get_codecs(PurpleMediaBackend *self, | |
1837 const gchar *sess_id) | |
1838 { | |
1839 PurpleMediaBackendFs2Private *priv; | |
1840 PurpleMediaBackendFs2Session *session; | |
1841 GList *fscodecs; | |
1842 GList *codecs; | |
1843 | |
1844 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); | |
1845 | |
1846 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
1847 | |
1848 session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id); | |
1849 | |
1850 if (session == NULL) | |
1851 return NULL; | |
1852 | |
1853 g_object_get(G_OBJECT(session->session), | |
1854 "codecs", &fscodecs, NULL); | |
1855 codecs = codec_list_from_fs(fscodecs); | |
1856 fs_codec_list_destroy(fscodecs); | |
1857 | |
1858 return codecs; | |
1859 } | |
1860 | |
1861 static GList * | |
1862 purple_media_backend_fs2_get_local_candidates(PurpleMediaBackend *self, | |
1863 const gchar *sess_id, const gchar *participant) | |
1864 { | |
1865 PurpleMediaBackendFs2Stream *stream; | |
1866 GList *candidates = NULL; | |
1867 | |
1868 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), NULL); | |
1869 | |
1870 stream = get_stream(PURPLE_MEDIA_BACKEND_FS2(self), | |
1871 sess_id, participant); | |
1872 | |
1873 if (stream != NULL) | |
1874 candidates = candidate_list_from_fs( | |
1875 stream->local_candidates); | |
1876 return candidates; | |
1877 } | |
1878 | |
1879 static gboolean | |
1880 purple_media_backend_fs2_set_remote_codecs(PurpleMediaBackend *self, | |
1881 const gchar *sess_id, const gchar *participant, | |
1882 GList *codecs) | |
1883 { | |
1884 PurpleMediaBackendFs2Stream *stream; | |
1885 GList *fscodecs; | |
1886 GError *err = NULL; | |
1887 | |
1888 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE); | |
1889 stream = get_stream(PURPLE_MEDIA_BACKEND_FS2(self), | |
1890 sess_id, participant); | |
1891 | |
1892 if (stream == NULL) | |
1893 return FALSE; | |
1894 | |
1895 fscodecs = codec_list_to_fs(codecs); | |
1896 fs_stream_set_remote_codecs(stream->stream, fscodecs, &err); | |
1897 fs_codec_list_destroy(fscodecs); | |
1898 | |
1899 if (err) { | |
1900 purple_debug_error("backend-fs2", | |
1901 "Error setting remote codecs: %s\n", | |
1902 err->message); | |
1903 g_error_free(err); | |
1904 return FALSE; | |
1905 } | |
1906 | |
1907 return TRUE; | |
1908 } | |
1909 | |
1910 static gboolean | |
1911 purple_media_backend_fs2_set_send_codec(PurpleMediaBackend *self, | |
1912 const gchar *sess_id, PurpleMediaCodec *codec) | |
1913 { | |
1914 PurpleMediaBackendFs2Session *session; | |
1915 FsCodec *fscodec; | |
1916 GError *err = NULL; | |
1917 | |
1918 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self), FALSE); | |
1919 | |
1920 session = get_session(PURPLE_MEDIA_BACKEND_FS2(self), sess_id); | |
1921 | |
1922 if (session == NULL) | |
1923 return FALSE; | |
1924 | |
1925 fscodec = codec_to_fs(codec); | |
1926 fs_session_set_send_codec(session->session, fscodec, &err); | |
1927 fs_codec_destroy(fscodec); | |
1928 | |
1929 if (err) { | |
1930 purple_debug_error("media", "Error setting send codec\n"); | |
1931 g_error_free(err); | |
1932 return FALSE; | |
1933 } | |
1934 | |
1935 return TRUE; | |
1936 } | |
1937 #else | |
1938 GType | |
1939 purple_media_backend_fs2_get_type(void) | |
1940 { | |
1941 return G_TYPE_NONE; | |
1942 } | |
1943 #endif /* USE_VV */ | |
1944 | |
1945 #ifdef USE_GSTREAMER | |
1946 GstElement * | |
1947 purple_media_backend_fs2_get_src(PurpleMediaBackendFs2 *self, | |
1948 const gchar *sess_id) | |
1949 { | |
1950 #ifdef USE_VV | |
1951 PurpleMediaBackendFs2Session *session = get_session(self, sess_id); | |
1952 return session != NULL ? session->src : NULL; | |
1953 #else | |
1954 return NULL; | |
1955 #endif | |
1956 } | |
1957 | |
1958 GstElement * | |
1959 purple_media_backend_fs2_get_tee(PurpleMediaBackendFs2 *self, | |
1960 const gchar *sess_id, const gchar *who) | |
1961 { | |
1962 #ifdef USE_VV | |
1963 if (sess_id != NULL && who == NULL) { | |
1964 PurpleMediaBackendFs2Session *session = | |
1965 get_session(self, sess_id); | |
1966 return (session != NULL) ? session->tee : NULL; | |
1967 } else if (sess_id != NULL && who != NULL) { | |
1968 PurpleMediaBackendFs2Stream *stream = | |
1969 get_stream(self, sess_id, who); | |
1970 return (stream != NULL) ? stream->tee : NULL; | |
1971 } | |
1972 | |
1973 #endif /* USE_VV */ | |
1974 g_return_val_if_reached(NULL); | |
1975 } | |
1976 | |
1977 void | |
1978 purple_media_backend_fs2_set_input_volume(PurpleMediaBackendFs2 *self, | |
1979 const gchar *sess_id, double level) | |
1980 { | |
1981 #ifdef USE_VV | |
1982 PurpleMediaBackendFs2Private *priv; | |
1983 GList *sessions; | |
1984 | |
1985 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self)); | |
1986 | |
1987 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
1988 | |
1989 purple_prefs_set_int("/purple/media/audio/volume/input", level); | |
1990 | |
1991 if (sess_id == NULL) | |
1992 sessions = g_hash_table_get_values(priv->sessions); | |
1993 else | |
1994 sessions = g_list_append(NULL, get_session(self, sess_id)); | |
1995 | |
1996 for (; sessions; sessions = g_list_delete_link(sessions, sessions)) { | |
1997 PurpleMediaBackendFs2Session *session = sessions->data; | |
1998 | |
1999 if (session->type & PURPLE_MEDIA_SEND_AUDIO) { | |
2000 gchar *name = g_strdup_printf("volume_%s", | |
2001 session->id); | |
2002 GstElement *volume = gst_bin_get_by_name( | |
2003 GST_BIN(priv->confbin), name); | |
2004 g_free(name); | |
2005 g_object_set(volume, "volume", level/10.0, NULL); | |
2006 } | |
2007 } | |
2008 #endif /* USE_VV */ | |
2009 } | |
2010 | |
2011 void | |
2012 purple_media_backend_fs2_set_output_volume(PurpleMediaBackendFs2 *self, | |
2013 const gchar *sess_id, const gchar *who, double level) | |
2014 { | |
2015 #ifdef USE_VV | |
2016 PurpleMediaBackendFs2Private *priv; | |
2017 GList *streams; | |
2018 | |
2019 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self)); | |
2020 | |
2021 priv = PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); | |
2022 | |
2023 purple_prefs_set_int("/purple/media/audio/volume/output", level); | |
2024 | |
2025 streams = get_streams(self, sess_id, who); | |
2026 | |
2027 for (; streams; streams = g_list_delete_link(streams, streams)) { | |
2028 PurpleMediaBackendFs2Stream *stream = streams->data; | |
2029 | |
2030 if (stream->session->type & PURPLE_MEDIA_RECV_AUDIO | |
2031 && GST_IS_ELEMENT(stream->volume)) { | |
2032 g_object_set(stream->volume, "volume", | |
2033 level/10.0, NULL); | |
2034 } | |
2035 } | |
2036 #endif /* USE_VV */ | |
2037 } | |
2038 #endif /* USE_GSTREAMER */ |