3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "glibcompat.h"
25 #include "backend-fs2.h"
28 #include "backend-iface.h"
31 #include "media-gst.h"
33 #include <farstream/fs-conference.h>
34 #include <farstream/fs-element-added-notifier.h>
35 #include <farstream/fs-utils.h>
36 #include <gst/gststructure.h>
38 /** @copydoc _PurpleMediaBackendFs2Session */
39 typedef struct _PurpleMediaBackendFs2Session PurpleMediaBackendFs2Session
;
40 /** @copydoc _PurpleMediaBackendFs2Stream */
41 typedef struct _PurpleMediaBackendFs2Stream PurpleMediaBackendFs2Stream
;
43 static void purple_media_backend_iface_init(PurpleMediaBackendInterface
*iface
);
46 gst_bus_cb(GstBus
*bus
, GstMessage
*msg
, PurpleMediaBackendFs2
*self
);
48 state_changed_cb(PurpleMedia
*media
, PurpleMediaState state
,
49 gchar
*sid
, gchar
*name
, PurpleMediaBackendFs2
*self
);
51 stream_info_cb(PurpleMedia
*media
, PurpleMediaInfoType type
,
52 gchar
*sid
, gchar
*name
, gboolean local
,
53 PurpleMediaBackendFs2
*self
);
55 static gboolean
purple_media_backend_fs2_add_stream(PurpleMediaBackend
*self
,
56 const gchar
*sess_id
, const gchar
*who
,
57 PurpleMediaSessionType type
, gboolean initiator
,
58 const gchar
*transmitter
,
59 guint num_params
, GParameter
*params
);
60 static void purple_media_backend_fs2_add_remote_candidates(
61 PurpleMediaBackend
*self
,
62 const gchar
*sess_id
, const gchar
*participant
,
63 GList
*remote_candidates
);
64 static gboolean
purple_media_backend_fs2_codecs_ready(PurpleMediaBackend
*self
,
65 const gchar
*sess_id
);
66 static GList
*purple_media_backend_fs2_get_codecs(PurpleMediaBackend
*self
,
67 const gchar
*sess_id
);
68 static GList
*purple_media_backend_fs2_get_local_candidates(
69 PurpleMediaBackend
*self
,
70 const gchar
*sess_id
, const gchar
*participant
);
71 static gboolean
purple_media_backend_fs2_set_encryption_parameters (
72 PurpleMediaBackend
*self
, const gchar
*sess_id
, const gchar
*cipher
,
73 const gchar
*auth
, const gchar
*key
, gsize key_len
);
74 static gboolean
purple_media_backend_fs2_set_decryption_parameters(
75 PurpleMediaBackend
*self
, const gchar
*sess_id
,
76 const gchar
*participant
, const gchar
*cipher
,
77 const gchar
*auth
, const gchar
*key
, gsize key_len
);
78 static gboolean
purple_media_backend_fs2_set_remote_codecs(
79 PurpleMediaBackend
*self
,
80 const gchar
*sess_id
, const gchar
*participant
,
82 static gboolean
purple_media_backend_fs2_set_send_codec(
83 PurpleMediaBackend
*self
, const gchar
*sess_id
,
84 PurpleMediaCodec
*codec
);
85 static void purple_media_backend_fs2_set_params(PurpleMediaBackend
*self
,
86 guint num_params
, GParameter
*params
);
87 static const gchar
**purple_media_backend_fs2_get_available_params(void);
88 static gboolean
purple_media_backend_fs2_send_dtmf(
89 PurpleMediaBackend
*self
, const gchar
*sess_id
,
90 gchar dtmf
, guint8 volume
, guint16 duration
);
91 static gboolean
purple_media_backend_fs2_set_send_rtcp_mux(
92 PurpleMediaBackend
*self
,
93 const gchar
*sess_id
, const gchar
*participant
,
94 gboolean send_rtcp_mux
);
96 static void free_stream(PurpleMediaBackendFs2Stream
*stream
);
97 static void free_session(PurpleMediaBackendFs2Session
*session
);
100 * PurpleMediaBackendFs2:
102 * An opaque structure representing the Farstream media backend.
104 struct _PurpleMediaBackendFs2
109 struct _PurpleMediaBackendFs2Stream
111 PurpleMediaBackendFs2Session
*session
;
115 gboolean supports_add
;
121 GstElement
*fakesink
;
124 GList
*local_candidates
;
125 GList
*remote_candidates
;
127 guint connected_cb_id
;
130 struct _PurpleMediaBackendFs2Session
132 PurpleMediaBackendFs2
*backend
;
138 GstElement
*srcvalve
;
142 PurpleMediaSessionType type
;
149 FsConference
*conference
;
150 gchar
*conference_type
;
152 FsElementAddedNotifier
*notifier
;
154 GHashTable
*sessions
;
155 GHashTable
*participants
;
159 gdouble silence_threshold
;
160 } PurpleMediaBackendFs2Private
;
164 PROP_CONFERENCE_TYPE
,
168 G_DEFINE_TYPE_WITH_CODE(PurpleMediaBackendFs2
, purple_media_backend_fs2
,
170 G_ADD_PRIVATE(PurpleMediaBackendFs2
)
171 G_IMPLEMENT_INTERFACE(PURPLE_TYPE_MEDIA_BACKEND
,
172 purple_media_backend_iface_init
));
175 purple_media_backend_fs2_init(PurpleMediaBackendFs2
*self
)
179 static FsCandidateType
180 purple_media_candidate_type_to_fs(PurpleMediaCandidateType type
)
183 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST
:
184 return FS_CANDIDATE_TYPE_HOST
;
185 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
:
186 return FS_CANDIDATE_TYPE_SRFLX
;
187 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
:
188 return FS_CANDIDATE_TYPE_PRFLX
;
189 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
:
190 return FS_CANDIDATE_TYPE_RELAY
;
191 case PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST
:
192 return FS_CANDIDATE_TYPE_MULTICAST
;
194 g_return_val_if_reached(FS_CANDIDATE_TYPE_HOST
);
197 static PurpleMediaCandidateType
198 purple_media_candidate_type_from_fs(FsCandidateType type
)
201 case FS_CANDIDATE_TYPE_HOST
:
202 return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
203 case FS_CANDIDATE_TYPE_SRFLX
:
204 return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
;
205 case FS_CANDIDATE_TYPE_PRFLX
:
206 return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
;
207 case FS_CANDIDATE_TYPE_RELAY
:
208 return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
;
209 case FS_CANDIDATE_TYPE_MULTICAST
:
210 return PURPLE_MEDIA_CANDIDATE_TYPE_MULTICAST
;
212 g_return_val_if_reached(PURPLE_MEDIA_CANDIDATE_TYPE_HOST
);
215 static FsNetworkProtocol
216 purple_media_network_protocol_to_fs(PurpleMediaNetworkProtocol protocol
)
219 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
:
220 return FS_NETWORK_PROTOCOL_UDP
;
221 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE
:
222 return FS_NETWORK_PROTOCOL_TCP_PASSIVE
;
223 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE
:
224 return FS_NETWORK_PROTOCOL_TCP_ACTIVE
;
225 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_SO
:
226 return FS_NETWORK_PROTOCOL_TCP_SO
;
228 g_return_val_if_reached(FS_NETWORK_PROTOCOL_TCP
);
232 static PurpleMediaNetworkProtocol
233 purple_media_network_protocol_from_fs(FsNetworkProtocol protocol
)
236 case FS_NETWORK_PROTOCOL_UDP
:
237 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
;
238 case FS_NETWORK_PROTOCOL_TCP_PASSIVE
:
239 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE
;
240 case FS_NETWORK_PROTOCOL_TCP_ACTIVE
:
241 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE
;
242 case FS_NETWORK_PROTOCOL_TCP_SO
:
243 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_SO
;
245 g_return_val_if_reached(PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE
);
249 static GstPadProbeReturn
250 event_probe_cb(GstPad
*srcpad
, GstPadProbeInfo
*info
, gpointer unused
)
252 GstEvent
*event
= GST_PAD_PROBE_INFO_EVENT(info
);
253 if (GST_EVENT_TYPE(event
) == GST_EVENT_CUSTOM_DOWNSTREAM
254 && gst_event_has_name(event
, "purple-unlink-tee")) {
256 const GstStructure
*s
= gst_event_get_structure(event
);
258 gst_pad_unlink(srcpad
, gst_pad_get_peer(srcpad
));
260 gst_pad_remove_probe(srcpad
,
261 g_value_get_ulong(gst_structure_get_value(s
, "handler-id")));
263 if (g_value_get_boolean(gst_structure_get_value(s
, "release-pad")))
264 gst_element_release_request_pad(GST_ELEMENT_PARENT(srcpad
), srcpad
);
266 return GST_PAD_PROBE_DROP
;
269 return GST_PAD_PROBE_OK
;
273 unlink_teepad_dynamic(GstPad
*srcpad
, gboolean release_pad
)
275 gulong id
= gst_pad_add_probe(srcpad
, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM
,
276 event_probe_cb
, NULL
, NULL
);
278 if (GST_IS_GHOST_PAD(srcpad
))
279 srcpad
= gst_ghost_pad_get_target(GST_GHOST_PAD(srcpad
));
281 gst_element_send_event(gst_pad_get_parent_element(srcpad
),
282 gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM
,
283 gst_structure_new("purple-unlink-tee",
284 "release-pad", G_TYPE_BOOLEAN
, release_pad
,
285 "handler-id", G_TYPE_ULONG
, id
,
290 purple_media_backend_fs2_dispose(GObject
*obj
)
292 PurpleMediaBackendFs2Private
*priv
=
293 purple_media_backend_fs2_get_instance_private(
294 PURPLE_MEDIA_BACKEND_FS2(obj
));
297 purple_debug_info("backend-fs2", "purple_media_backend_fs2_dispose\n");
299 if (priv
->notifier
) {
300 g_object_unref(priv
->notifier
);
301 priv
->notifier
= NULL
;
305 GstElement
*pipeline
;
307 pipeline
= purple_media_manager_get_pipeline(
308 purple_media_get_manager(priv
->media
));
310 /* All connections to media sources should be blocked before confbin is
311 * removed, to prevent freezing of any other simultaneously running
313 if (priv
->sessions
) {
314 GList
*sessions
= g_hash_table_get_values(priv
->sessions
);
315 for (; sessions
; sessions
=
316 g_list_delete_link(sessions
, sessions
)) {
317 PurpleMediaBackendFs2Session
*session
= sessions
->data
;
318 if (session
->srcpad
) {
319 unlink_teepad_dynamic(session
->srcpad
, FALSE
);
320 gst_object_unref(session
->srcpad
);
321 session
->srcpad
= NULL
;
326 gst_element_set_locked_state(priv
->confbin
, TRUE
);
327 gst_element_set_state(GST_ELEMENT(priv
->confbin
),
332 gst_bin_remove(GST_BIN(pipeline
), priv
->confbin
);
333 bus
= gst_pipeline_get_bus(GST_PIPELINE(pipeline
));
334 g_signal_handlers_disconnect_matched(G_OBJECT(bus
),
335 G_SIGNAL_MATCH_FUNC
|
337 0, 0, 0, gst_bus_cb
, obj
);
338 gst_object_unref(bus
);
340 purple_debug_warning("backend-fs2", "Unable to "
341 "properly dispose the conference. "
342 "Couldn't get the pipeline.\n");
345 priv
->confbin
= NULL
;
346 priv
->conference
= NULL
;
350 if (priv
->sessions
) {
351 GList
*sessions
= g_hash_table_get_values(priv
->sessions
);
353 for (; sessions
; sessions
=
354 g_list_delete_link(sessions
, sessions
)) {
355 PurpleMediaBackendFs2Session
*session
=
358 if (session
->session
) {
359 g_object_unref(session
->session
);
360 session
->session
= NULL
;
365 if (priv
->participants
) {
366 g_hash_table_destroy(priv
->participants
);
367 priv
->participants
= NULL
;
370 for (iter
= priv
->streams
; iter
; iter
= g_list_next(iter
)) {
371 PurpleMediaBackendFs2Stream
*stream
= iter
->data
;
372 if (stream
->stream
) {
373 g_object_unref(stream
->stream
);
374 stream
->stream
= NULL
;
379 g_object_remove_weak_pointer(G_OBJECT(priv
->media
),
380 (gpointer
*)&priv
->media
);
384 G_OBJECT_CLASS(purple_media_backend_fs2_parent_class
)->dispose(obj
);
388 purple_media_backend_fs2_finalize(GObject
*obj
)
390 PurpleMediaBackendFs2Private
*priv
=
391 purple_media_backend_fs2_get_instance_private(
392 PURPLE_MEDIA_BACKEND_FS2(obj
));
394 purple_debug_info("backend-fs2", "purple_media_backend_fs2_finalize\n");
396 g_free(priv
->conference_type
);
398 for (; priv
->streams
; priv
->streams
=
399 g_list_delete_link(priv
->streams
, priv
->streams
)) {
400 PurpleMediaBackendFs2Stream
*stream
= priv
->streams
->data
;
404 if (priv
->sessions
) {
405 GList
*sessions
= g_hash_table_get_values(priv
->sessions
);
407 for (; sessions
; sessions
=
408 g_list_delete_link(sessions
, sessions
)) {
409 PurpleMediaBackendFs2Session
*session
=
411 free_session(session
);
414 g_hash_table_destroy(priv
->sessions
);
417 G_OBJECT_CLASS(purple_media_backend_fs2_parent_class
)->finalize(obj
);
421 purple_media_backend_fs2_set_property(GObject
*object
, guint prop_id
,
422 const GValue
*value
, GParamSpec
*pspec
)
424 PurpleMediaBackendFs2Private
*priv
;
425 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object
));
427 priv
= purple_media_backend_fs2_get_instance_private(
428 PURPLE_MEDIA_BACKEND_FS2(object
));
431 case PROP_CONFERENCE_TYPE
:
432 priv
->conference_type
= g_value_dup_string(value
);
435 priv
->media
= g_value_get_object(value
);
437 if (priv
->media
== NULL
)
440 g_object_add_weak_pointer(G_OBJECT(priv
->media
),
441 (gpointer
*)&priv
->media
);
443 g_signal_connect(G_OBJECT(priv
->media
),
445 G_CALLBACK(state_changed_cb
),
446 PURPLE_MEDIA_BACKEND_FS2(object
));
447 g_signal_connect(G_OBJECT(priv
->media
), "stream-info",
448 G_CALLBACK(stream_info_cb
),
449 PURPLE_MEDIA_BACKEND_FS2(object
));
452 G_OBJECT_WARN_INVALID_PROPERTY_ID(
453 object
, prop_id
, pspec
);
459 purple_media_backend_fs2_get_property(GObject
*object
, guint prop_id
,
460 GValue
*value
, GParamSpec
*pspec
)
462 PurpleMediaBackendFs2Private
*priv
;
463 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object
));
465 priv
= purple_media_backend_fs2_get_instance_private(
466 PURPLE_MEDIA_BACKEND_FS2(object
));
469 case PROP_CONFERENCE_TYPE
:
470 g_value_set_string(value
, priv
->conference_type
);
473 g_value_set_object(value
, priv
->media
);
476 G_OBJECT_WARN_INVALID_PROPERTY_ID(
477 object
, prop_id
, pspec
);
483 purple_media_backend_fs2_class_init(PurpleMediaBackendFs2Class
*klass
)
485 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
489 gobject_class
->dispose
= purple_media_backend_fs2_dispose
;
490 gobject_class
->finalize
= purple_media_backend_fs2_finalize
;
491 gobject_class
->set_property
= purple_media_backend_fs2_set_property
;
492 gobject_class
->get_property
= purple_media_backend_fs2_get_property
;
494 g_object_class_override_property(gobject_class
, PROP_CONFERENCE_TYPE
,
496 g_object_class_override_property(gobject_class
, PROP_MEDIA
, "media");
498 /* VA-API elements aren't well supported in Farstream. Ignore them. */
499 features
= gst_registry_get_feature_list_by_plugin(gst_registry_get(),
501 g_list_foreach(features
, (GFunc
)gst_plugin_feature_set_rank
, GINT_TO_POINTER(GST_RANK_NONE
));
502 gst_plugin_feature_list_free(features
);
506 purple_media_backend_iface_init(PurpleMediaBackendInterface
*iface
)
508 iface
->add_stream
= purple_media_backend_fs2_add_stream
;
509 iface
->add_remote_candidates
=
510 purple_media_backend_fs2_add_remote_candidates
;
511 iface
->codecs_ready
= purple_media_backend_fs2_codecs_ready
;
512 iface
->get_codecs
= purple_media_backend_fs2_get_codecs
;
513 iface
->get_local_candidates
=
514 purple_media_backend_fs2_get_local_candidates
;
515 iface
->set_remote_codecs
= purple_media_backend_fs2_set_remote_codecs
;
516 iface
->set_send_codec
= purple_media_backend_fs2_set_send_codec
;
517 iface
->set_encryption_parameters
=
518 purple_media_backend_fs2_set_encryption_parameters
;
519 iface
->set_decryption_parameters
=
520 purple_media_backend_fs2_set_decryption_parameters
;
521 iface
->set_params
= purple_media_backend_fs2_set_params
;
522 iface
->get_available_params
= purple_media_backend_fs2_get_available_params
;
523 iface
->send_dtmf
= purple_media_backend_fs2_send_dtmf
;
524 iface
->set_send_rtcp_mux
= purple_media_backend_fs2_set_send_rtcp_mux
;
528 session_type_to_fs_media_type(PurpleMediaSessionType type
)
530 if (type
& PURPLE_MEDIA_AUDIO
)
531 return FS_MEDIA_TYPE_AUDIO
;
532 else if (type
& PURPLE_MEDIA_VIDEO
)
533 return FS_MEDIA_TYPE_VIDEO
;
534 #ifdef HAVE_MEDIA_APPLICATION
535 else if (type
& PURPLE_MEDIA_APPLICATION
)
536 return FS_MEDIA_TYPE_APPLICATION
;
542 static FsStreamDirection
543 session_type_to_fs_stream_direction(PurpleMediaSessionType type
)
545 if ((type
& PURPLE_MEDIA_AUDIO
) == PURPLE_MEDIA_AUDIO
||
546 (type
& PURPLE_MEDIA_VIDEO
) == PURPLE_MEDIA_VIDEO
)
547 return FS_DIRECTION_BOTH
;
548 else if ((type
& PURPLE_MEDIA_SEND_AUDIO
) ||
549 (type
& PURPLE_MEDIA_SEND_VIDEO
))
550 return FS_DIRECTION_SEND
;
551 else if ((type
& PURPLE_MEDIA_RECV_AUDIO
) ||
552 (type
& PURPLE_MEDIA_RECV_VIDEO
))
553 return FS_DIRECTION_RECV
;
554 #ifdef HAVE_MEDIA_APPLICATION
555 else if ((type
& PURPLE_MEDIA_APPLICATION
) == PURPLE_MEDIA_APPLICATION
)
556 return FS_DIRECTION_BOTH
;
557 else if (type
& PURPLE_MEDIA_SEND_APPLICATION
)
558 return FS_DIRECTION_SEND
;
559 else if (type
& PURPLE_MEDIA_RECV_APPLICATION
)
560 return FS_DIRECTION_RECV
;
563 return FS_DIRECTION_NONE
;
566 static PurpleMediaSessionType
567 session_type_from_fs(FsMediaType type
, FsStreamDirection direction
)
569 PurpleMediaSessionType result
= PURPLE_MEDIA_NONE
;
570 if (type
== FS_MEDIA_TYPE_AUDIO
) {
571 if (direction
& FS_DIRECTION_SEND
)
572 result
|= PURPLE_MEDIA_SEND_AUDIO
;
573 if (direction
& FS_DIRECTION_RECV
)
574 result
|= PURPLE_MEDIA_RECV_AUDIO
;
575 } else if (type
== FS_MEDIA_TYPE_VIDEO
) {
576 if (direction
& FS_DIRECTION_SEND
)
577 result
|= PURPLE_MEDIA_SEND_VIDEO
;
578 if (direction
& FS_DIRECTION_RECV
)
579 result
|= PURPLE_MEDIA_RECV_VIDEO
;
580 #ifdef HAVE_MEDIA_APPLICATION
581 } else if (type
== FS_MEDIA_TYPE_APPLICATION
) {
582 if (direction
& FS_DIRECTION_SEND
)
583 result
|= PURPLE_MEDIA_SEND_APPLICATION
;
584 if (direction
& FS_DIRECTION_RECV
)
585 result
|= PURPLE_MEDIA_RECV_APPLICATION
;
592 candidate_to_fs(PurpleMediaCandidate
*candidate
)
594 FsCandidate
*fscandidate
;
601 PurpleMediaNetworkProtocol proto
;
603 PurpleMediaCandidateType type
;
608 if (candidate
== NULL
)
611 g_object_get(G_OBJECT(candidate
),
612 "foundation", &foundation
,
613 "component-id", &component_id
,
617 "base-port", &base_port
,
619 "priority", &priority
,
621 "username", &username
,
622 "password", &password
,
626 fscandidate
= fs_candidate_new(foundation
,
627 component_id
, purple_media_candidate_type_to_fs(type
),
628 purple_media_network_protocol_to_fs(proto
), ip
, port
);
630 fscandidate
->base_ip
= base_ip
;
631 fscandidate
->base_port
= base_port
;
632 fscandidate
->priority
= priority
;
633 fscandidate
->username
= username
;
634 fscandidate
->password
= password
;
635 fscandidate
->ttl
= ttl
;
643 candidate_list_to_fs(GList
*candidates
)
645 GList
*new_list
= NULL
;
647 for (; candidates
; candidates
= g_list_next(candidates
)) {
648 new_list
= g_list_prepend(new_list
,
649 candidate_to_fs(candidates
->data
));
652 new_list
= g_list_reverse(new_list
);
656 static PurpleMediaCandidate
*
657 candidate_from_fs(FsCandidate
*fscandidate
)
659 PurpleMediaCandidate
*candidate
;
661 if (fscandidate
== NULL
)
664 candidate
= purple_media_candidate_new(fscandidate
->foundation
,
665 fscandidate
->component_id
,
666 purple_media_candidate_type_from_fs(fscandidate
->type
),
667 purple_media_network_protocol_from_fs(fscandidate
->proto
),
668 fscandidate
->ip
, fscandidate
->port
);
669 g_object_set(candidate
,
670 "base-ip", fscandidate
->base_ip
,
671 "base-port", fscandidate
->base_port
,
672 "priority", fscandidate
->priority
,
673 "username", fscandidate
->username
,
674 "password", fscandidate
->password
,
675 "ttl", fscandidate
->ttl
, NULL
);
680 candidate_list_from_fs(GList
*candidates
)
682 GList
*new_list
= NULL
;
684 for (; candidates
; candidates
= g_list_next(candidates
)) {
685 new_list
= g_list_prepend(new_list
,
686 candidate_from_fs(candidates
->data
));
689 new_list
= g_list_reverse(new_list
);
694 codec_to_fs(const PurpleMediaCodec
*codec
)
699 PurpleMediaSessionType media_type
;
707 g_object_get(G_OBJECT(codec
),
709 "encoding-name", &encoding_name
,
710 "media-type", &media_type
,
711 "clock-rate", &clock_rate
,
712 "channels", &channels
,
713 "optional-params", &iter
,
716 new_codec
= fs_codec_new(id
, encoding_name
,
717 session_type_to_fs_media_type(media_type
),
719 new_codec
->channels
= channels
;
721 for (; iter
; iter
= g_list_next(iter
)) {
722 PurpleKeyValuePair
*param
= (PurpleKeyValuePair
*)iter
->data
;
723 fs_codec_add_optional_parameter(new_codec
,
724 param
->key
, param
->value
);
727 g_free(encoding_name
);
731 static PurpleMediaCodec
*
732 codec_from_fs(const FsCodec
*codec
)
734 PurpleMediaCodec
*new_codec
;
740 new_codec
= purple_media_codec_new(codec
->id
, codec
->encoding_name
,
741 session_type_from_fs(codec
->media_type
,
742 FS_DIRECTION_BOTH
), codec
->clock_rate
);
743 g_object_set(new_codec
, "channels", codec
->channels
, NULL
);
745 for (iter
= codec
->optional_params
; iter
; iter
= g_list_next(iter
)) {
746 FsCodecParameter
*param
= (FsCodecParameter
*)iter
->data
;
747 purple_media_codec_add_optional_parameter(new_codec
,
748 param
->name
, param
->value
);
755 codec_list_from_fs(GList
*codecs
)
757 GList
*new_list
= NULL
;
759 for (; codecs
; codecs
= g_list_next(codecs
)) {
760 new_list
= g_list_prepend(new_list
,
761 codec_from_fs(codecs
->data
));
764 new_list
= g_list_reverse(new_list
);
769 codec_list_to_fs(GList
*codecs
)
771 GList
*new_list
= NULL
;
773 for (; codecs
; codecs
= g_list_next(codecs
)) {
774 new_list
= g_list_prepend(new_list
,
775 codec_to_fs(codecs
->data
));
778 new_list
= g_list_reverse(new_list
);
782 static PurpleMediaBackendFs2Session
*
783 get_session(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
)
785 PurpleMediaBackendFs2Private
*priv
;
786 PurpleMediaBackendFs2Session
*session
= NULL
;
788 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
790 priv
= purple_media_backend_fs2_get_instance_private(self
);
792 if (priv
->sessions
!= NULL
)
793 session
= g_hash_table_lookup(priv
->sessions
, sess_id
);
798 static FsParticipant
*
799 get_participant(PurpleMediaBackendFs2
*self
, const gchar
*name
)
801 PurpleMediaBackendFs2Private
*priv
;
802 FsParticipant
*participant
= NULL
;
804 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
806 priv
= purple_media_backend_fs2_get_instance_private(self
);
808 if (priv
->participants
!= NULL
)
809 participant
= g_hash_table_lookup(priv
->participants
, name
);
814 static PurpleMediaBackendFs2Stream
*
815 get_stream(PurpleMediaBackendFs2
*self
,
816 const gchar
*sess_id
, const gchar
*name
)
818 PurpleMediaBackendFs2Private
*priv
;
821 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
823 priv
= purple_media_backend_fs2_get_instance_private(self
);
824 streams
= priv
->streams
;
826 for (; streams
; streams
= g_list_next(streams
)) {
827 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
828 if (purple_strequal(stream
->session
->id
, sess_id
) &&
829 purple_strequal(stream
->participant
, name
))
837 get_streams(PurpleMediaBackendFs2
*self
,
838 const gchar
*sess_id
, const gchar
*name
)
840 PurpleMediaBackendFs2Private
*priv
;
841 GList
*streams
, *ret
= NULL
;
843 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
845 priv
= purple_media_backend_fs2_get_instance_private(self
);
846 streams
= priv
->streams
;
848 for (; streams
; streams
= g_list_next(streams
)) {
849 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
851 if (sess_id
!= NULL
&& !purple_strequal(stream
->session
->id
, sess_id
))
853 else if (name
!= NULL
&& !purple_strequal(stream
->participant
, name
))
856 ret
= g_list_prepend(ret
, stream
);
859 ret
= g_list_reverse(ret
);
863 static PurpleMediaBackendFs2Session
*
864 get_session_from_fs_stream(PurpleMediaBackendFs2
*self
, FsStream
*stream
)
866 PurpleMediaBackendFs2Private
*priv
=
867 purple_media_backend_fs2_get_instance_private(self
);
868 FsSession
*fssession
;
871 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
872 g_return_val_if_fail(FS_IS_STREAM(stream
), NULL
);
874 g_object_get(stream
, "session", &fssession
, NULL
);
876 values
= g_hash_table_get_values(priv
->sessions
);
878 for (; values
; values
= g_list_delete_link(values
, values
)) {
879 PurpleMediaBackendFs2Session
*session
= values
->data
;
881 if (session
->session
== fssession
) {
883 g_object_unref(fssession
);
888 g_object_unref(fssession
);
893 gst_msg_db_to_percent(GstMessage
*msg
, gchar
*value_name
)
900 list
= gst_structure_get_value(gst_message_get_structure(msg
), value_name
);
901 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
902 value
= g_value_array_get_nth(g_value_get_boxed(list
), 0);
903 G_GNUC_END_IGNORE_DEPRECATIONS
904 value_db
= g_value_get_double(value
);
905 percent
= pow(10, value_db
/ 20);
906 return (percent
> 1.0) ? 1.0 : percent
;
910 purple_media_error_fs(PurpleMedia
*media
, const gchar
*error
,
911 const GstStructure
*fs_error
)
913 const gchar
*error_msg
= gst_structure_get_string(fs_error
, "error-msg");
915 purple_media_error(media
, "%s%s%s", error
,
916 error_msg
? _("\n\nMessage from Farstream: ") : "",
917 error_msg
? error_msg
: "");
921 gst_handle_message_element(GstBus
*bus
, GstMessage
*msg
,
922 PurpleMediaBackendFs2
*self
)
924 PurpleMediaBackendFs2Private
*priv
=
925 purple_media_backend_fs2_get_instance_private(self
);
926 GstElement
*src
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
927 static guint level_id
= 0;
928 const GstStructure
*structure
= gst_message_get_structure(msg
);
931 level_id
= g_signal_lookup("level", PURPLE_TYPE_MEDIA
);
933 if (gst_structure_has_name(structure
, "level")) {
934 GstElement
*src
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
936 gchar
*participant
= NULL
;
937 PurpleMediaBackendFs2Session
*session
= NULL
;
940 if (!PURPLE_IS_MEDIA(priv
->media
) ||
941 GST_ELEMENT_PARENT(src
) != priv
->confbin
)
944 name
= gst_element_get_name(src
);
946 if (!strncmp(name
, "sendlevel_", 10)) {
947 session
= get_session(self
, name
+10);
948 if (priv
->silence_threshold
> 0) {
949 percent
= gst_msg_db_to_percent(msg
, "decay");
950 g_object_set(session
->srcvalve
,
951 "drop", (percent
< priv
->silence_threshold
), NULL
);
957 if (!g_signal_has_handler_pending(priv
->media
, level_id
, 0, FALSE
))
961 GList
*iter
= priv
->streams
;
962 PurpleMediaBackendFs2Stream
*stream
;
963 for (; iter
; iter
= g_list_next(iter
)) {
965 if (stream
->level
== src
) {
966 session
= stream
->session
;
967 participant
= stream
->participant
;
976 percent
= gst_msg_db_to_percent(msg
, "rms");
978 g_signal_emit(priv
->media
, level_id
, 0,
979 session
->id
, participant
, percent
);
983 if (!FS_IS_CONFERENCE(src
) || !PURPLE_IS_MEDIA_BACKEND(self
) ||
984 priv
->conference
!= FS_CONFERENCE(src
))
987 if (gst_structure_has_name(structure
, "farstream-error")) {
989 gboolean error_emitted
= FALSE
;
990 gst_structure_get_enum(structure
, "error-no",
991 FS_TYPE_ERROR
, (gint
*)&error_no
);
993 case FS_ERROR_CONSTRUCTION
:
994 purple_media_error_fs(priv
->media
,
995 _("Error initializing the call. "
996 "This probably denotes problem in "
997 "installation of GStreamer or Farstream."),
999 error_emitted
= TRUE
;
1001 case FS_ERROR_NETWORK
:
1002 purple_media_error_fs(priv
->media
, _("Network error."),
1004 error_emitted
= TRUE
;
1005 purple_media_end(priv
->media
, NULL
, NULL
);
1007 case FS_ERROR_NEGOTIATION_FAILED
:
1008 purple_media_error_fs(priv
->media
,
1009 _("Codec negotiation failed. "
1010 "This problem might be resolved by installing "
1011 "more GStreamer codecs."),
1013 error_emitted
= TRUE
;
1014 purple_media_end(priv
->media
, NULL
, NULL
);
1016 case FS_ERROR_NO_CODECS
:
1017 purple_media_error(priv
->media
,
1018 _("No codecs found. "
1019 "Install some GStreamer codecs found "
1020 "in GStreamer plugins packages."));
1021 error_emitted
= TRUE
;
1022 purple_media_end(priv
->media
, NULL
, NULL
);
1025 purple_debug_error("backend-fs2",
1026 "farstream-error: %i: %s\n",
1028 gst_structure_get_string(structure
, "error-msg"));
1032 if (FS_ERROR_IS_FATAL(error_no
)) {
1034 purple_media_error(priv
->media
,
1035 _("A non-recoverable Farstream error has occurred."));
1036 purple_media_end(priv
->media
, NULL
, NULL
);
1038 } else if (gst_structure_has_name(structure
,
1039 "farstream-new-local-candidate")) {
1040 const GValue
*value
;
1042 FsCandidate
*local_candidate
;
1043 PurpleMediaCandidate
*candidate
;
1044 FsParticipant
*participant
;
1045 PurpleMediaBackendFs2Session
*session
;
1046 PurpleMediaBackendFs2Stream
*media_stream
;
1049 value
= gst_structure_get_value(structure
, "stream");
1050 stream
= g_value_get_object(value
);
1051 value
= gst_structure_get_value(structure
, "candidate");
1052 local_candidate
= g_value_get_boxed(value
);
1054 session
= get_session_from_fs_stream(self
, stream
);
1056 purple_debug_info("backend-fs2",
1057 "got new local candidate: %s\n",
1058 local_candidate
->foundation
);
1060 g_object_get(stream
, "participant", &participant
, NULL
);
1061 name
= g_object_get_data(G_OBJECT(participant
), "purple-name");
1063 media_stream
= get_stream(self
, session
->id
, name
);
1064 media_stream
->local_candidates
= g_list_append(
1065 media_stream
->local_candidates
,
1066 fs_candidate_copy(local_candidate
));
1068 candidate
= candidate_from_fs(local_candidate
);
1069 g_signal_emit_by_name(self
, "new-candidate",
1070 session
->id
, name
, candidate
);
1071 g_object_unref(candidate
);
1072 g_object_unref(participant
);
1073 } else if (gst_structure_has_name(structure
,
1074 "farstream-local-candidates-prepared")) {
1075 const GValue
*value
;
1077 FsParticipant
*participant
;
1078 PurpleMediaBackendFs2Session
*session
;
1080 value
= gst_structure_get_value(structure
, "stream");
1081 stream
= g_value_get_object(value
);
1082 session
= get_session_from_fs_stream(self
, stream
);
1084 g_object_get(stream
, "participant", &participant
, NULL
);
1086 g_signal_emit_by_name(self
, "candidates-prepared",
1088 g_object_get_data(G_OBJECT(participant
), "purple-name"));
1090 g_object_unref(participant
);
1091 } else if (gst_structure_has_name(structure
,
1092 "farstream-new-active-candidate-pair")) {
1093 const GValue
*value
;
1095 FsCandidate
*local_candidate
;
1096 FsCandidate
*remote_candidate
;
1097 FsParticipant
*participant
;
1098 PurpleMediaBackendFs2Session
*session
;
1099 PurpleMediaCandidate
*lcandidate
, *rcandidate
;
1101 value
= gst_structure_get_value(structure
, "stream");
1102 stream
= g_value_get_object(value
);
1103 value
= gst_structure_get_value(structure
, "local-candidate");
1104 local_candidate
= g_value_get_boxed(value
);
1105 value
= gst_structure_get_value(structure
, "remote-candidate");
1106 remote_candidate
= g_value_get_boxed(value
);
1108 g_object_get(stream
, "participant", &participant
, NULL
);
1110 session
= get_session_from_fs_stream(self
, stream
);
1112 lcandidate
= candidate_from_fs(local_candidate
);
1113 rcandidate
= candidate_from_fs(remote_candidate
);
1115 g_signal_emit_by_name(self
, "active-candidate-pair",
1117 g_object_get_data(G_OBJECT(participant
), "purple-name"),
1118 lcandidate
, rcandidate
);
1120 g_object_unref(participant
);
1121 g_object_unref(lcandidate
);
1122 g_object_unref(rcandidate
);
1123 } else if (gst_structure_has_name(structure
,
1124 "farstream-recv-codecs-changed")) {
1125 const GValue
*value
;
1129 value
= gst_structure_get_value(structure
, "codecs");
1130 codecs
= g_value_get_boxed(value
);
1131 codec
= codecs
->data
;
1133 purple_debug_info("backend-fs2",
1134 "farstream-recv-codecs-changed: %s\n",
1135 codec
->encoding_name
);
1136 } else if (gst_structure_has_name(structure
,
1137 "farstream-component-state-changed")) {
1138 const GValue
*value
;
1139 FsStreamState fsstate
;
1143 value
= gst_structure_get_value(structure
, "state");
1144 fsstate
= g_value_get_enum(value
);
1145 value
= gst_structure_get_value(structure
, "component");
1146 component
= g_value_get_uint(value
);
1149 case FS_STREAM_STATE_FAILED
:
1152 case FS_STREAM_STATE_DISCONNECTED
:
1153 state
= "DISCONNECTED";
1155 case FS_STREAM_STATE_GATHERING
:
1156 state
= "GATHERING";
1158 case FS_STREAM_STATE_CONNECTING
:
1159 state
= "CONNECTING";
1161 case FS_STREAM_STATE_CONNECTED
:
1162 state
= "CONNECTED";
1164 case FS_STREAM_STATE_READY
:
1172 purple_debug_info("backend-fs2",
1173 "farstream-component-state-changed: "
1174 "component: %u state: %s\n",
1176 } else if (gst_structure_has_name(structure
,
1177 "farstream-send-codec-changed")) {
1178 const GValue
*value
;
1182 value
= gst_structure_get_value(structure
, "codec");
1183 codec
= g_value_get_boxed(value
);
1184 codec_str
= fs_codec_to_string(codec
);
1186 purple_debug_info("backend-fs2",
1187 "farstream-send-codec-changed: codec: %s\n",
1191 } else if (gst_structure_has_name(structure
,
1192 "farstream-codecs-changed")) {
1193 const GValue
*value
;
1194 FsSession
*fssession
;
1197 value
= gst_structure_get_value(structure
, "session");
1198 fssession
= g_value_get_object(value
);
1199 sessions
= g_hash_table_get_values(priv
->sessions
);
1201 for (; sessions
; sessions
=
1202 g_list_delete_link(sessions
, sessions
)) {
1203 PurpleMediaBackendFs2Session
*session
= sessions
->data
;
1206 if (session
->session
!= fssession
)
1209 session_id
= g_strdup(session
->id
);
1210 g_signal_emit_by_name(self
, "codecs-changed",
1213 g_list_free(sessions
);
1220 gst_handle_message_error(GstBus
*bus
, GstMessage
*msg
,
1221 PurpleMediaBackendFs2
*self
)
1223 PurpleMediaBackendFs2Private
*priv
=
1224 purple_media_backend_fs2_get_instance_private(self
);
1225 GstElement
*element
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
1226 GstElement
*lastElement
= NULL
;
1229 GError
*error
= NULL
;
1230 gchar
*debug_msg
= NULL
;
1232 gst_message_parse_error(msg
, &error
, &debug_msg
);
1233 purple_debug_error("backend-fs2", "gst error %s\ndebugging: %s\n",
1234 error
->message
, debug_msg
);
1236 g_error_free(error
);
1239 while (element
&& !GST_IS_PIPELINE(element
)) {
1240 if (element
== priv
->confbin
)
1243 lastElement
= element
;
1244 element
= GST_ELEMENT_PARENT(element
);
1247 if (!element
|| !GST_IS_PIPELINE(element
))
1250 sessions
= purple_media_get_session_ids(priv
->media
);
1252 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
1253 if (purple_media_get_src(priv
->media
, sessions
->data
)
1257 if (purple_media_get_session_type(priv
->media
, sessions
->data
)
1258 & PURPLE_MEDIA_AUDIO
)
1259 purple_media_error(priv
->media
,
1260 _("Error with your microphone"));
1261 else if (purple_media_get_session_type(priv
->media
,
1262 sessions
->data
) & PURPLE_MEDIA_VIDEO
)
1263 purple_media_error(priv
->media
,
1264 _("Error with your webcam"));
1269 g_list_free(sessions
);
1271 purple_media_error(priv
->media
, _("Conference error"));
1272 purple_media_end(priv
->media
, NULL
, NULL
);
1276 gst_bus_cb(GstBus
*bus
, GstMessage
*msg
, PurpleMediaBackendFs2
*self
)
1278 switch(GST_MESSAGE_TYPE(msg
)) {
1279 case GST_MESSAGE_ELEMENT
:
1280 gst_handle_message_element(bus
, msg
, self
);
1282 case GST_MESSAGE_ERROR
:
1283 gst_handle_message_error(bus
, msg
, self
);
1293 remove_element(GstElement
*element
)
1296 gst_element_set_locked_state(element
, TRUE
);
1297 gst_element_set_state(element
, GST_STATE_NULL
);
1298 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(element
)), element
);
1303 state_changed_cb(PurpleMedia
*media
, PurpleMediaState state
,
1304 gchar
*sid
, gchar
*name
, PurpleMediaBackendFs2
*self
)
1306 if (state
== PURPLE_MEDIA_STATE_END
) {
1307 PurpleMediaBackendFs2Private
*priv
=
1308 purple_media_backend_fs2_get_instance_private(
1312 PurpleMediaBackendFs2Stream
*stream
= get_stream(self
, sid
, name
);
1313 gst_object_unref(stream
->stream
);
1315 priv
->streams
= g_list_remove(priv
->streams
, stream
);
1317 remove_element(stream
->src
);
1318 remove_element(stream
->tee
);
1319 remove_element(stream
->volume
);
1320 remove_element(stream
->level
);
1321 remove_element(stream
->fakesink
);
1322 remove_element(stream
->queue
);
1324 free_stream(stream
);
1325 } else if (sid
&& !name
) {
1326 PurpleMediaBackendFs2Session
*session
= get_session(self
, sid
);
1329 g_object_get(session
->session
, "sink-pad", &pad
, NULL
);
1330 gst_pad_unlink(GST_PAD_PEER(pad
), pad
);
1331 gst_object_unref(pad
);
1333 gst_object_unref(session
->session
);
1334 g_hash_table_remove(priv
->sessions
, session
->id
);
1336 pad
= gst_pad_get_peer(session
->srcpad
);
1337 gst_element_remove_pad(GST_ELEMENT_PARENT(pad
), pad
);
1338 gst_object_unref(pad
);
1339 gst_object_unref(session
->srcpad
);
1341 remove_element(session
->srcvalve
);
1342 remove_element(session
->tee
);
1344 free_session(session
);
1347 purple_media_manager_remove_output_windows(
1348 purple_media_get_manager(media
), media
, sid
, name
);
1353 stream_info_cb(PurpleMedia
*media
, PurpleMediaInfoType type
,
1354 gchar
*sid
, gchar
*name
, gboolean local
,
1355 PurpleMediaBackendFs2
*self
)
1357 if (type
== PURPLE_MEDIA_INFO_ACCEPT
&& sid
!= NULL
&& name
!= NULL
) {
1358 PurpleMediaBackendFs2Stream
*stream
=
1359 get_stream(self
, sid
, name
);
1362 g_object_set(G_OBJECT(stream
->stream
), "direction",
1363 session_type_to_fs_stream_direction(
1364 stream
->session
->type
), NULL
);
1366 if (stream
->remote_candidates
== NULL
||
1367 purple_media_is_initiator(media
, sid
, name
))
1370 if (stream
->supports_add
)
1371 fs_stream_add_remote_candidates(stream
->stream
,
1372 stream
->remote_candidates
, &err
);
1374 fs_stream_force_remote_candidates(stream
->stream
,
1375 stream
->remote_candidates
, &err
);
1380 purple_debug_error("backend-fs2", "Error adding "
1381 "remote candidates: %s\n",
1384 } else if (local
== TRUE
&& (type
== PURPLE_MEDIA_INFO_MUTE
||
1385 type
== PURPLE_MEDIA_INFO_UNMUTE
)) {
1386 PurpleMediaBackendFs2Private
*priv
=
1387 purple_media_backend_fs2_get_instance_private(
1389 gboolean active
= (type
== PURPLE_MEDIA_INFO_MUTE
);
1393 sessions
= g_hash_table_get_values(priv
->sessions
);
1395 sessions
= g_list_prepend(NULL
,
1396 get_session(self
, sid
));
1398 purple_debug_info("media", "Turning mute %s\n",
1399 active
? "on" : "off");
1401 for (; sessions
; sessions
= g_list_delete_link(
1402 sessions
, sessions
)) {
1403 PurpleMediaBackendFs2Session
*session
=
1406 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
1407 gchar
*name
= g_strdup_printf("volume_%s",
1409 GstElement
*volume
= gst_bin_get_by_name(
1410 GST_BIN(priv
->confbin
), name
);
1412 g_object_set(volume
, "mute", active
, NULL
);
1415 } else if (local
== TRUE
&& (type
== PURPLE_MEDIA_INFO_HOLD
||
1416 type
== PURPLE_MEDIA_INFO_UNHOLD
)) {
1417 gboolean active
= (type
== PURPLE_MEDIA_INFO_HOLD
);
1418 GList
*streams
= get_streams(self
, sid
, name
);
1419 for (; streams
; streams
=
1420 g_list_delete_link(streams
, streams
)) {
1421 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
1422 if (stream
->session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
1423 g_object_set(stream
->stream
, "direction",
1424 session_type_to_fs_stream_direction(
1425 stream
->session
->type
& ((active
) ?
1426 ~PURPLE_MEDIA_SEND_AUDIO
:
1427 PURPLE_MEDIA_AUDIO
)), NULL
);
1430 } else if (local
== TRUE
&& (type
== PURPLE_MEDIA_INFO_PAUSE
||
1431 type
== PURPLE_MEDIA_INFO_UNPAUSE
)) {
1432 gboolean active
= (type
== PURPLE_MEDIA_INFO_PAUSE
);
1433 GList
*streams
= get_streams(self
, sid
, name
);
1434 for (; streams
; streams
=
1435 g_list_delete_link(streams
, streams
)) {
1436 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
1437 if (stream
->session
->type
& PURPLE_MEDIA_SEND_VIDEO
) {
1438 g_object_set(stream
->stream
, "direction",
1439 session_type_to_fs_stream_direction(
1440 stream
->session
->type
& ((active
) ?
1441 ~PURPLE_MEDIA_SEND_VIDEO
:
1442 PURPLE_MEDIA_VIDEO
)), NULL
);
1449 init_conference(PurpleMediaBackendFs2
*self
)
1451 PurpleMediaBackendFs2Private
*priv
=
1452 purple_media_backend_fs2_get_instance_private(self
);
1453 GstElement
*pipeline
;
1456 GKeyFile
*default_props
;
1458 priv
->conference
= FS_CONFERENCE(
1459 gst_element_factory_make(priv
->conference_type
, NULL
));
1461 if (priv
->conference
== NULL
) {
1462 purple_debug_error("backend-fs2", "Conference == NULL\n");
1466 if (purple_account_get_silence_suppression(
1467 purple_media_get_account(priv
->media
)))
1468 priv
->silence_threshold
= purple_prefs_get_int(
1469 "/purple/media/audio/silence_threshold") / 100.0;
1471 priv
->silence_threshold
= 0;
1473 pipeline
= purple_media_manager_get_pipeline(
1474 purple_media_get_manager(priv
->media
));
1476 if (pipeline
== NULL
) {
1477 purple_debug_error("backend-fs2",
1478 "Couldn't retrieve pipeline.\n");
1482 name
= g_strdup_printf("conf_%p", priv
->conference
);
1483 priv
->confbin
= gst_bin_new(name
);
1484 if (priv
->confbin
== NULL
) {
1485 purple_debug_error("backend-fs2",
1486 "Couldn't create confbin.\n");
1492 bus
= gst_pipeline_get_bus(GST_PIPELINE(pipeline
));
1494 purple_debug_error("backend-fs2",
1495 "Couldn't get the pipeline's bus.\n");
1499 default_props
= fs_utils_get_default_element_properties(GST_ELEMENT(priv
->conference
));
1500 if (default_props
!= NULL
) {
1501 priv
->notifier
= fs_element_added_notifier_new();
1502 fs_element_added_notifier_add(priv
->notifier
,
1503 GST_BIN(priv
->confbin
));
1504 fs_element_added_notifier_set_properties_from_keyfile(priv
->notifier
, default_props
);
1507 g_signal_connect(G_OBJECT(bus
), "message",
1508 G_CALLBACK(gst_bus_cb
), self
);
1509 gst_object_unref(bus
);
1511 if (!gst_bin_add(GST_BIN(pipeline
),
1512 GST_ELEMENT(priv
->confbin
))) {
1513 purple_debug_error("backend-fs2", "Couldn't add confbin "
1514 "element to the pipeline\n");
1518 if (!gst_bin_add(GST_BIN(priv
->confbin
),
1519 GST_ELEMENT(priv
->conference
))) {
1520 purple_debug_error("backend-fs2", "Couldn't add conference "
1521 "element to the confbin\n");
1525 if (gst_element_set_state(GST_ELEMENT(priv
->confbin
),
1526 GST_STATE_PLAYING
) == GST_STATE_CHANGE_FAILURE
) {
1527 purple_debug_error("backend-fs2",
1528 "Failed to start conference.\n");
1536 create_src(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
,
1537 PurpleMediaSessionType type
)
1539 PurpleMediaBackendFs2Private
*priv
=
1540 purple_media_backend_fs2_get_instance_private(self
);
1541 PurpleMediaBackendFs2Session
*session
;
1542 PurpleMediaSessionType session_type
;
1543 FsMediaType media_type
= session_type_to_fs_media_type(type
);
1544 FsStreamDirection type_direction
=
1545 session_type_to_fs_stream_direction(type
);
1547 GstPad
*sinkpad
, *srcpad
;
1548 GstPad
*ghost
= NULL
;
1550 if ((type_direction
& FS_DIRECTION_SEND
) == 0)
1553 session_type
= session_type_from_fs(
1554 media_type
, FS_DIRECTION_SEND
);
1555 src
= purple_media_manager_get_element(
1556 purple_media_get_manager(priv
->media
),
1557 session_type
, priv
->media
, sess_id
, NULL
);
1559 if (!GST_IS_ELEMENT(src
)) {
1560 purple_debug_error("backend-fs2",
1561 "Error creating src for session %s\n",
1566 session
= get_session(self
, sess_id
);
1568 if (session
== NULL
) {
1569 purple_debug_warning("backend-fs2",
1570 "purple_media_set_src: trying to set"
1571 " src on non-existent session\n");
1576 gst_object_unref(session
->src
);
1579 gst_element_set_locked_state(session
->src
, TRUE
);
1581 session
->tee
= gst_element_factory_make("tee", NULL
);
1582 gst_bin_add(GST_BIN(priv
->confbin
), session
->tee
);
1584 /* This supposedly isn't necessary, but it silences some warnings */
1585 if (GST_ELEMENT_PARENT(priv
->confbin
)
1586 == GST_ELEMENT_PARENT(session
->src
)) {
1587 GstPad
*pad
= gst_element_get_static_pad(session
->tee
, "sink");
1588 ghost
= gst_ghost_pad_new(NULL
, pad
);
1589 gst_object_unref(pad
);
1590 gst_pad_set_active(ghost
, TRUE
);
1591 gst_element_add_pad(priv
->confbin
, ghost
);
1594 gst_element_set_state(session
->tee
, GST_STATE_PLAYING
);
1595 gst_element_link(session
->src
, priv
->confbin
);
1597 session
->srcpad
= gst_pad_get_peer(ghost
);
1599 g_object_get(session
->session
, "sink-pad", &sinkpad
, NULL
);
1600 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
1601 gchar
*name
= g_strdup_printf("volume_%s", session
->id
);
1603 GstElement
*volume
= gst_element_factory_make("volume", name
);
1604 double input_volume
= purple_prefs_get_int(
1605 "/purple/media/audio/volume/input")/10.0;
1607 name
= g_strdup_printf("sendlevel_%s", session
->id
);
1608 level
= gst_element_factory_make("level", name
);
1610 session
->srcvalve
= gst_element_factory_make("valve", NULL
);
1611 gst_bin_add(GST_BIN(priv
->confbin
), volume
);
1612 gst_bin_add(GST_BIN(priv
->confbin
), level
);
1613 gst_bin_add(GST_BIN(priv
->confbin
), session
->srcvalve
);
1614 gst_element_set_state(level
, GST_STATE_PLAYING
);
1615 gst_element_set_state(volume
, GST_STATE_PLAYING
);
1616 gst_element_set_state(session
->srcvalve
, GST_STATE_PLAYING
);
1617 gst_element_link(level
, session
->srcvalve
);
1618 gst_element_link(volume
, level
);
1619 gst_element_link(session
->tee
, volume
);
1620 srcpad
= gst_element_get_static_pad(session
->srcvalve
, "src");
1621 g_object_set(volume
, "volume", input_volume
, NULL
);
1623 srcpad
= gst_element_get_request_pad(session
->tee
, "src_%u");
1626 purple_debug_info("backend-fs2", "connecting pad: %s\n",
1627 gst_pad_link(srcpad
, sinkpad
) == GST_PAD_LINK_OK
1628 ? "success" : "failure");
1629 gst_element_set_locked_state(session
->src
, FALSE
);
1630 gst_object_unref(session
->src
);
1631 gst_object_unref(sinkpad
);
1633 purple_media_manager_create_output_window(purple_media_get_manager(
1634 priv
->media
), priv
->media
, sess_id
, NULL
);
1636 purple_debug_info("backend-fs2", "create_src: setting source "
1637 "state to GST_STATE_PLAYING - it may hang here on win32\n");
1638 gst_element_set_state(session
->src
, GST_STATE_PLAYING
);
1639 purple_debug_info("backend-fs2", "create_src: state set\n");
1645 create_session(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
,
1646 PurpleMediaSessionType type
, gboolean initiator
,
1647 const gchar
*transmitter
)
1649 PurpleMediaBackendFs2Private
*priv
=
1650 purple_media_backend_fs2_get_instance_private(self
);
1651 PurpleMediaBackendFs2Session
*session
;
1653 GList
*codec_conf
= NULL
;
1654 gchar
*filename
= NULL
;
1656 session
= g_new0(PurpleMediaBackendFs2Session
, 1);
1658 session
->session
= fs_conference_new_session(priv
->conference
,
1659 session_type_to_fs_media_type(type
), &err
);
1661 #ifdef HAVE_MEDIA_APPLICATION
1662 if (type
== PURPLE_MEDIA_APPLICATION
) {
1664 GObject
*rtpsession
= NULL
;
1666 caps
= gst_caps_new_empty_simple ("application/octet-stream");
1667 fs_session_set_allowed_caps (session
->session
, caps
, caps
, NULL
);
1668 gst_caps_unref (caps
);
1669 g_object_get (session
->session
, "internal-session", &rtpsession
, NULL
);
1671 g_object_set (rtpsession
, "probation", 0, NULL
);
1672 g_object_unref (rtpsession
);
1677 purple_media_error(priv
->media
,
1678 _("Error creating session: %s"),
1685 filename
= g_build_filename(purple_config_dir(), "fs-codec.conf", NULL
);
1686 codec_conf
= fs_codec_list_from_keyfile(filename
, &err
);
1690 if (err
->code
== G_KEY_FILE_ERROR_NOT_FOUND
)
1691 purple_debug_info("backend-fs2", "Couldn't read "
1692 "fs-codec.conf: %s\n",
1695 purple_debug_error("backend-fs2", "Error reading "
1696 "fs-codec.conf: %s\n",
1700 purple_debug_info("backend-fs2",
1701 "Loading default codec conf instead\n");
1702 codec_conf
= fs_utils_get_default_codec_preferences(
1703 GST_ELEMENT(priv
->conference
));
1706 fs_session_set_codec_preferences(session
->session
, codec_conf
, NULL
);
1707 fs_codec_list_destroy(codec_conf
);
1710 * Removes a 5-7 second delay before
1711 * receiving the src-pad-added signal.
1712 * Only works for non-multicast FsRtpSessions.
1714 if (!purple_strequal(transmitter
, "multicast"))
1715 g_object_set(G_OBJECT(session
->session
),
1716 "no-rtcp-timeout", 0, NULL
);
1718 session
->id
= g_strdup(sess_id
);
1719 session
->backend
= self
;
1720 session
->type
= type
;
1722 if (!priv
->sessions
) {
1723 purple_debug_info("backend-fs2",
1724 "Creating hash table for sessions\n");
1725 priv
->sessions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1729 g_hash_table_insert(priv
->sessions
, g_strdup(session
->id
), session
);
1731 if (!create_src(self
, sess_id
, type
)) {
1732 purple_debug_info("backend-fs2", "Error creating the src\n");
1740 free_session(PurpleMediaBackendFs2Session
*session
)
1742 g_free(session
->id
);
1747 create_participant(PurpleMediaBackendFs2
*self
, const gchar
*name
)
1749 PurpleMediaBackendFs2Private
*priv
=
1750 purple_media_backend_fs2_get_instance_private(self
);
1751 FsParticipant
*participant
;
1754 participant
= fs_conference_new_participant(
1755 priv
->conference
, &err
);
1758 purple_debug_error("backend-fs2",
1759 "Error creating participant: %s\n",
1765 g_object_set_data_full(G_OBJECT(participant
), "purple-name",
1766 g_strdup(name
), g_free
);
1768 if (g_object_class_find_property(G_OBJECT_GET_CLASS(participant
),
1770 g_object_set(participant
, "cname", name
, NULL
);
1773 if (!priv
->participants
) {
1774 purple_debug_info("backend-fs2",
1775 "Creating hash table for participants\n");
1776 priv
->participants
= g_hash_table_new_full(g_str_hash
,
1777 g_str_equal
, g_free
, g_object_unref
);
1780 g_hash_table_insert(priv
->participants
, g_strdup(name
), participant
);
1786 src_pad_added_cb_cb(PurpleMediaBackendFs2Stream
*stream
)
1788 PurpleMediaBackendFs2Private
*priv
;
1790 g_return_val_if_fail(stream
!= NULL
, FALSE
);
1792 priv
= purple_media_backend_fs2_get_instance_private(
1793 stream
->session
->backend
);
1794 stream
->connected_cb_id
= 0;
1796 purple_media_manager_create_output_window(
1797 purple_media_get_manager(priv
->media
), priv
->media
,
1798 stream
->session
->id
, stream
->participant
);
1800 g_signal_emit_by_name(priv
->media
, "state-changed",
1801 PURPLE_MEDIA_STATE_CONNECTED
,
1802 stream
->session
->id
, stream
->participant
);
1807 src_pad_added_cb(FsStream
*fsstream
, GstPad
*srcpad
,
1808 FsCodec
*codec
, PurpleMediaBackendFs2Stream
*stream
)
1810 PurpleMediaBackendFs2Private
*priv
;
1813 g_return_if_fail(FS_IS_STREAM(fsstream
));
1814 g_return_if_fail(stream
!= NULL
);
1816 priv
= purple_media_backend_fs2_get_instance_private(
1817 stream
->session
->backend
);
1819 if (stream
->src
== NULL
) {
1820 GstElement
*sink
= NULL
;
1822 if (codec
->media_type
== FS_MEDIA_TYPE_AUDIO
) {
1823 double output_volume
= purple_prefs_get_int(
1824 "/purple/media/audio/volume/output")/10.0;
1826 * Should this instead be:
1827 * audioconvert ! audioresample ! liveadder !
1828 * audioresample ! audioconvert ! realsink
1830 stream
->queue
= gst_element_factory_make("queue", NULL
);
1831 stream
->volume
= gst_element_factory_make("volume", NULL
);
1832 g_object_set(stream
->volume
, "volume", output_volume
, NULL
);
1833 stream
->level
= gst_element_factory_make("level", NULL
);
1834 stream
->src
= gst_element_factory_make("liveadder", NULL
);
1835 sink
= purple_media_manager_get_element(
1836 purple_media_get_manager(priv
->media
),
1837 PURPLE_MEDIA_RECV_AUDIO
, priv
->media
,
1838 stream
->session
->id
,
1839 stream
->participant
);
1840 gst_bin_add(GST_BIN(priv
->confbin
), stream
->queue
);
1841 gst_bin_add(GST_BIN(priv
->confbin
), stream
->volume
);
1842 gst_bin_add(GST_BIN(priv
->confbin
), stream
->level
);
1843 gst_bin_add(GST_BIN(priv
->confbin
), sink
);
1844 gst_element_set_state(sink
, GST_STATE_PLAYING
);
1845 gst_element_set_state(stream
->level
, GST_STATE_PLAYING
);
1846 gst_element_set_state(stream
->volume
, GST_STATE_PLAYING
);
1847 gst_element_set_state(stream
->queue
, GST_STATE_PLAYING
);
1848 gst_element_link(stream
->level
, sink
);
1849 gst_element_link(stream
->volume
, stream
->level
);
1850 gst_element_link(stream
->queue
, stream
->volume
);
1851 sink
= stream
->queue
;
1852 } else if (codec
->media_type
== FS_MEDIA_TYPE_VIDEO
) {
1853 stream
->src
= gst_element_factory_make("funnel", NULL
);
1854 sink
= gst_element_factory_make("fakesink", NULL
);
1855 g_object_set(G_OBJECT(sink
), "async", FALSE
, NULL
);
1856 gst_bin_add(GST_BIN(priv
->confbin
), sink
);
1857 gst_element_set_state(sink
, GST_STATE_PLAYING
);
1858 stream
->fakesink
= sink
;
1859 #ifdef HAVE_MEDIA_APPLICATION
1860 } else if (codec
->media_type
== FS_MEDIA_TYPE_APPLICATION
) {
1861 stream
->src
= gst_element_factory_make("funnel", NULL
);
1862 sink
= purple_media_manager_get_element(
1863 purple_media_get_manager(priv
->media
),
1864 PURPLE_MEDIA_RECV_APPLICATION
, priv
->media
,
1865 stream
->session
->id
,
1866 stream
->participant
);
1867 gst_bin_add(GST_BIN(priv
->confbin
), sink
);
1868 gst_element_set_state(sink
, GST_STATE_PLAYING
);
1871 stream
->tee
= gst_element_factory_make("tee", NULL
);
1872 gst_bin_add_many(GST_BIN(priv
->confbin
),
1873 stream
->src
, stream
->tee
, NULL
);
1874 gst_element_set_state(stream
->tee
, GST_STATE_PLAYING
);
1875 gst_element_set_state(stream
->src
, GST_STATE_PLAYING
);
1876 gst_element_link_many(stream
->src
, stream
->tee
, sink
, NULL
);
1879 sinkpad
= gst_element_get_request_pad(stream
->src
, "sink_%u");
1880 gst_pad_link(srcpad
, sinkpad
);
1881 gst_object_unref(sinkpad
);
1883 stream
->connected_cb_id
= g_timeout_add(0,
1884 (GSourceFunc
)src_pad_added_cb_cb
, stream
);
1888 create_stream(PurpleMediaBackendFs2
*self
,
1889 const gchar
*sess_id
, const gchar
*who
,
1890 PurpleMediaSessionType type
, gboolean initiator
,
1891 const gchar
*transmitter
,
1892 guint num_params
, GParameter
*params
)
1894 PurpleMediaBackendFs2Private
*priv
=
1895 purple_media_backend_fs2_get_instance_private(self
);
1897 FsStream
*fsstream
= NULL
;
1898 const gchar
*stun_ip
= purple_network_get_stun_ip();
1899 const gchar
*turn_ip
= purple_network_get_turn_ip();
1900 guint _num_params
= num_params
;
1901 GParameter
*_params
;
1902 FsStreamDirection type_direction
=
1903 session_type_to_fs_stream_direction(type
);
1904 PurpleMediaBackendFs2Session
*session
;
1905 PurpleMediaBackendFs2Stream
*stream
;
1906 FsParticipant
*participant
;
1907 /* check if the protocol has already specified a relay-info
1908 we need to do this to allow them to override when using non-standard
1909 TURN modes, like Google f.ex. */
1910 gboolean got_turn_from_protocol
= FALSE
;
1912 GPtrArray
*relay_info
= g_ptr_array_new_full (1, (GDestroyNotify
) gst_structure_free
);
1915 session
= get_session(self
, sess_id
);
1917 if (session
== NULL
) {
1918 purple_debug_error("backend-fs2",
1919 "Couldn't find session to create stream.\n");
1923 participant
= get_participant(self
, who
);
1925 if (participant
== NULL
) {
1926 purple_debug_error("backend-fs2", "Couldn't find "
1927 "participant to create stream.\n");
1931 fsstream
= fs_session_new_stream(session
->session
, participant
,
1932 initiator
== TRUE
? type_direction
:
1933 (type_direction
& FS_DIRECTION_RECV
), &err
);
1935 if (fsstream
== NULL
) {
1937 purple_debug_error("backend-fs2", "Error creating stream: %s\n",
1938 err
->message
? err
->message
: "NULL");
1941 purple_debug_error("backend-fs2",
1942 "Error creating stream\n");
1946 for (i
= 0 ; i
< num_params
; i
++) {
1947 if (purple_strequal(params
[i
].name
, "relay-info")) {
1948 got_turn_from_protocol
= TRUE
;
1953 _params
= g_new0(GParameter
, num_params
+ 3);
1954 memcpy(_params
, params
, sizeof(GParameter
) * num_params
);
1956 /* set the controlling mode parameter */
1957 _params
[_num_params
].name
= "controlling-mode";
1958 g_value_init(&_params
[_num_params
].value
, G_TYPE_BOOLEAN
);
1959 g_value_set_boolean(&_params
[_num_params
].value
, initiator
);
1963 purple_debug_info("backend-fs2",
1964 "Setting stun-ip on new stream: %s\n", stun_ip
);
1966 _params
[_num_params
].name
= "stun-ip";
1967 g_value_init(&_params
[_num_params
].value
, G_TYPE_STRING
);
1968 g_value_set_string(&_params
[_num_params
].value
, stun_ip
);
1972 if (turn_ip
&& purple_strequal("nice", transmitter
) && !got_turn_from_protocol
) {
1974 const gchar
*username
= purple_prefs_get_string(
1975 "/purple/network/turn_username");
1976 const gchar
*password
= purple_prefs_get_string(
1977 "/purple/network/turn_password");
1980 port
= purple_prefs_get_int("/purple/network/turn_port");
1982 g_ptr_array_add (relay_info
,
1983 gst_structure_new ("relay-info",
1984 "ip", G_TYPE_STRING
, turn_ip
,
1985 "port", G_TYPE_UINT
, port
,
1986 "username", G_TYPE_STRING
, username
,
1987 "password", G_TYPE_STRING
, password
,
1988 "relay-type", G_TYPE_STRING
, "udp",
1993 port
= purple_prefs_get_int("/purple/network/turn_port_tcp");
1995 g_ptr_array_add (relay_info
,
1996 gst_structure_new ("relay-info",
1997 "ip", G_TYPE_STRING
, turn_ip
,
1998 "port", G_TYPE_UINT
, port
,
1999 "username", G_TYPE_STRING
, username
,
2000 "password", G_TYPE_STRING
, password
,
2001 "relay-type", G_TYPE_STRING
, "tcp",
2005 /* TURN over SSL is only supported by libnice for Google's "psuedo" SSL mode
2008 purple_debug_info("backend-fs2",
2009 "Setting relay-info on new stream\n");
2010 _params
[_num_params
].name
= "relay-info";
2011 g_value_init(&_params
[_num_params
].value
, G_TYPE_PTR_ARRAY
);
2012 g_value_set_boxed(&_params
[_num_params
].value
, relay_info
);
2016 ret
= fs_stream_set_transmitter(fsstream
, transmitter
,
2017 _params
, _num_params
, &err
);
2018 for (i
= 0 ; i
< _num_params
; i
++)
2019 g_value_unset (&_params
[i
].value
);
2022 g_ptr_array_unref (relay_info
);
2024 purple_debug_error("backend-fs2",
2025 "Could not set transmitter %s: %s.\n",
2026 transmitter
, err
? err
->message
: NULL
);
2027 g_clear_error(&err
);
2031 stream
= g_new0(PurpleMediaBackendFs2Stream
, 1);
2032 stream
->participant
= g_strdup(who
);
2033 stream
->session
= session
;
2034 stream
->stream
= fsstream
;
2035 stream
->supports_add
= purple_strequal(transmitter
, "nice");
2037 priv
->streams
= g_list_append(priv
->streams
, stream
);
2039 g_signal_connect(G_OBJECT(fsstream
), "src-pad-added",
2040 G_CALLBACK(src_pad_added_cb
), stream
);
2046 free_stream(PurpleMediaBackendFs2Stream
*stream
)
2048 /* Remove the connected_cb timeout */
2049 if (stream
->connected_cb_id
!= 0)
2050 g_source_remove(stream
->connected_cb_id
);
2052 g_free(stream
->participant
);
2054 if (stream
->local_candidates
)
2055 fs_candidate_list_destroy(stream
->local_candidates
);
2057 if (stream
->remote_candidates
)
2058 fs_candidate_list_destroy(stream
->remote_candidates
);
2064 purple_media_backend_fs2_add_stream(PurpleMediaBackend
*self
,
2065 const gchar
*sess_id
, const gchar
*who
,
2066 PurpleMediaSessionType type
, gboolean initiator
,
2067 const gchar
*transmitter
,
2068 guint num_params
, GParameter
*params
)
2070 PurpleMediaBackendFs2
*backend
= PURPLE_MEDIA_BACKEND_FS2(self
);
2071 PurpleMediaBackendFs2Private
*priv
=
2072 purple_media_backend_fs2_get_instance_private(backend
);
2073 PurpleMediaBackendFs2Stream
*stream
;
2075 if (priv
->conference
== NULL
&& !init_conference(backend
)) {
2076 purple_debug_error("backend-fs2",
2077 "Error initializing the conference.\n");
2081 if (get_session(backend
, sess_id
) == NULL
&&
2082 !create_session(backend
, sess_id
, type
,
2083 initiator
, transmitter
)) {
2084 purple_debug_error("backend-fs2",
2085 "Error creating the session.\n");
2089 if (get_participant(backend
, who
) == NULL
&&
2090 !create_participant(backend
, who
)) {
2091 purple_debug_error("backend-fs2",
2092 "Error creating the participant.\n");
2096 stream
= get_stream(backend
, sess_id
, who
);
2098 if (stream
!= NULL
) {
2099 FsStreamDirection type_direction
=
2100 session_type_to_fs_stream_direction(type
);
2102 if (session_type_to_fs_stream_direction(
2103 stream
->session
->type
) != type_direction
) {
2104 /* change direction */
2105 g_object_set(stream
->stream
, "direction",
2106 type_direction
, NULL
);
2108 } else if (!create_stream(backend
, sess_id
, who
, type
,
2109 initiator
, transmitter
, num_params
, params
)) {
2110 purple_debug_error("backend-fs2",
2111 "Error creating the stream.\n");
2119 purple_media_backend_fs2_add_remote_candidates(PurpleMediaBackend
*self
,
2120 const gchar
*sess_id
, const gchar
*participant
,
2121 GList
*remote_candidates
)
2123 PurpleMediaBackendFs2Private
*priv
;
2124 PurpleMediaBackendFs2Stream
*stream
;
2127 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2129 priv
= purple_media_backend_fs2_get_instance_private(
2130 PURPLE_MEDIA_BACKEND_FS2(self
));
2131 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2132 sess_id
, participant
);
2134 if (stream
== NULL
) {
2135 purple_debug_error("backend-fs2",
2136 "purple_media_add_remote_candidates: "
2137 "couldn't find stream %s %s.\n",
2138 sess_id
? sess_id
: "(null)",
2139 participant
? participant
: "(null)");
2143 stream
->remote_candidates
= g_list_concat(stream
->remote_candidates
,
2144 candidate_list_to_fs(remote_candidates
));
2146 if (purple_media_is_initiator(priv
->media
, sess_id
, participant
) ||
2147 purple_media_accepted(
2148 priv
->media
, sess_id
, participant
)) {
2150 if (stream
->supports_add
)
2151 fs_stream_add_remote_candidates(stream
->stream
,
2152 stream
->remote_candidates
, &err
);
2154 fs_stream_force_remote_candidates(stream
->stream
,
2155 stream
->remote_candidates
, &err
);
2158 purple_debug_error("backend-fs2", "Error adding remote"
2159 " candidates: %s\n", err
->message
);
2166 purple_media_backend_fs2_codecs_ready(PurpleMediaBackend
*self
,
2167 const gchar
*sess_id
)
2169 PurpleMediaBackendFs2Private
*priv
;
2170 gboolean ret
= FALSE
;
2172 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2174 priv
= purple_media_backend_fs2_get_instance_private(
2175 PURPLE_MEDIA_BACKEND_FS2(self
));
2177 if (sess_id
!= NULL
) {
2178 PurpleMediaBackendFs2Session
*session
= get_session(
2179 PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2181 if (session
== NULL
)
2184 if (session
->type
& (PURPLE_MEDIA_SEND_AUDIO
|
2185 #ifdef HAVE_MEDIA_APPLICATION
2186 PURPLE_MEDIA_SEND_APPLICATION
|
2188 PURPLE_MEDIA_SEND_VIDEO
)) {
2190 GList
*codecs
= NULL
;
2192 g_object_get(session
->session
,
2193 "codecs", &codecs
, NULL
);
2195 fs_codec_list_destroy (codecs
);
2201 GList
*values
= g_hash_table_get_values(priv
->sessions
);
2203 for (; values
; values
= g_list_delete_link(values
, values
)) {
2204 PurpleMediaBackendFs2Session
*session
= values
->data
;
2206 if (session
->type
& (PURPLE_MEDIA_SEND_AUDIO
|
2207 #ifdef HAVE_MEDIA_APPLICATION
2208 PURPLE_MEDIA_SEND_APPLICATION
|
2210 PURPLE_MEDIA_SEND_VIDEO
)) {
2212 GList
*codecs
= NULL
;
2214 g_object_get(session
->session
,
2215 "codecs", &codecs
, NULL
);
2217 fs_codec_list_destroy (codecs
);
2228 g_list_free(values
);
2235 purple_media_backend_fs2_get_codecs(PurpleMediaBackend
*self
,
2236 const gchar
*sess_id
)
2238 PurpleMediaBackendFs2Session
*session
;
2242 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
2244 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2246 if (session
== NULL
)
2249 g_object_get(G_OBJECT(session
->session
),
2250 "codecs", &fscodecs
, NULL
);
2251 codecs
= codec_list_from_fs(fscodecs
);
2252 fs_codec_list_destroy(fscodecs
);
2258 purple_media_backend_fs2_get_local_candidates(PurpleMediaBackend
*self
,
2259 const gchar
*sess_id
, const gchar
*participant
)
2261 PurpleMediaBackendFs2Stream
*stream
;
2262 GList
*candidates
= NULL
;
2264 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
2266 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2267 sess_id
, participant
);
2270 candidates
= candidate_list_from_fs(
2271 stream
->local_candidates
);
2276 purple_media_backend_fs2_set_remote_codecs(PurpleMediaBackend
*self
,
2277 const gchar
*sess_id
, const gchar
*participant
,
2280 PurpleMediaBackendFs2Stream
*stream
;
2284 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2285 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2286 sess_id
, participant
);
2291 fscodecs
= codec_list_to_fs(codecs
);
2292 fs_stream_set_remote_codecs(stream
->stream
, fscodecs
, &err
);
2293 fs_codec_list_destroy(fscodecs
);
2296 purple_debug_error("backend-fs2",
2297 "Error setting remote codecs: %s\n",
2306 static GstStructure
*
2307 create_fs2_srtp_structure(const gchar
*cipher
, const gchar
*auth
,
2308 const gchar
*key
, gsize key_len
)
2310 GstStructure
*result
;
2314 buffer
= gst_buffer_new_allocate(NULL
, key_len
, NULL
);
2315 gst_buffer_map(buffer
, &info
, GST_MAP_WRITE
);
2316 memcpy(info
.data
, key
, key_len
);
2317 gst_buffer_unmap(buffer
, &info
);
2319 result
= gst_structure_new("FarstreamSRTP",
2320 "cipher", G_TYPE_STRING
, cipher
,
2321 "auth", G_TYPE_STRING
, auth
,
2322 "key", GST_TYPE_BUFFER
, buffer
,
2324 gst_buffer_unref(buffer
);
2330 purple_media_backend_fs2_set_encryption_parameters (PurpleMediaBackend
*self
,
2331 const gchar
*sess_id
, const gchar
*cipher
, const gchar
*auth
,
2332 const gchar
*key
, gsize key_len
)
2334 PurpleMediaBackendFs2Session
*session
;
2339 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2341 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2345 srtp
= create_fs2_srtp_structure(cipher
, auth
, key
, key_len
);
2349 result
= fs_session_set_encryption_parameters(session
->session
, srtp
,
2352 purple_debug_error("backend-fs2",
2353 "Error setting encryption parameters: %s\n", err
->message
);
2357 gst_structure_free(srtp
);
2362 purple_media_backend_fs2_set_decryption_parameters (PurpleMediaBackend
*self
,
2363 const gchar
*sess_id
, const gchar
*participant
,
2364 const gchar
*cipher
, const gchar
*auth
,
2365 const gchar
*key
, gsize key_len
)
2367 PurpleMediaBackendFs2Stream
*stream
;
2372 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2374 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
,
2379 srtp
= create_fs2_srtp_structure(cipher
, auth
, key
, key_len
);
2383 result
= fs_stream_set_decryption_parameters(stream
->stream
, srtp
,
2386 purple_debug_error("backend-fs2",
2387 "Error setting decryption parameters: %s\n", err
->message
);
2391 gst_structure_free(srtp
);
2396 purple_media_backend_fs2_set_send_codec(PurpleMediaBackend
*self
,
2397 const gchar
*sess_id
, PurpleMediaCodec
*codec
)
2399 PurpleMediaBackendFs2Session
*session
;
2403 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2405 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2407 if (session
== NULL
)
2410 fscodec
= codec_to_fs(codec
);
2411 fs_session_set_send_codec(session
->session
, fscodec
, &err
);
2412 fs_codec_destroy(fscodec
);
2415 purple_debug_error("media", "Error setting send codec\n");
2423 static const gchar
**
2424 purple_media_backend_fs2_get_available_params(void)
2426 static const gchar
*supported_params
[] = {
2427 "sdes-cname", "sdes-email", "sdes-location", "sdes-name", "sdes-note",
2428 "sdes-phone", "sdes-tool", NULL
2431 return supported_params
;
2435 param_to_sdes_type(const gchar
*param
)
2437 const gchar
**supported
= purple_media_backend_fs2_get_available_params();
2438 static const gchar
*sdes_types
[] = {
2439 "cname", "email", "location", "name", "note", "phone", "tool", NULL
2443 for (i
= 0; supported
[i
] != NULL
; ++i
) {
2444 if (purple_strequal(param
, supported
[i
])) {
2445 return sdes_types
[i
];
2453 purple_media_backend_fs2_set_params(PurpleMediaBackend
*self
,
2454 guint num_params
, GParameter
*params
)
2456 PurpleMediaBackendFs2Private
*priv
;
2460 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2462 priv
= purple_media_backend_fs2_get_instance_private(
2463 PURPLE_MEDIA_BACKEND_FS2(self
));
2465 if (priv
->conference
== NULL
&&
2466 !init_conference(PURPLE_MEDIA_BACKEND_FS2(self
))) {
2467 purple_debug_error("backend-fs2",
2468 "Error initializing the conference.\n");
2472 g_object_get(G_OBJECT(priv
->conference
), "sdes", &sdes
, NULL
);
2474 for (i
= 0; i
!= num_params
; ++i
) {
2475 const gchar
*sdes_type
= param_to_sdes_type(params
[i
].name
);
2479 gst_structure_set(sdes
, sdes_type
,
2480 G_TYPE_STRING
, g_value_get_string(¶ms
[i
].value
),
2484 g_object_set(G_OBJECT(priv
->conference
), "sdes", sdes
, NULL
);
2485 gst_structure_free(sdes
);
2488 send_dtmf_callback(gpointer userdata
)
2490 FsSession
*session
= userdata
;
2492 fs_session_stop_telephony_event(session
);
2497 purple_media_backend_fs2_send_dtmf(PurpleMediaBackend
*self
,
2498 const gchar
*sess_id
, gchar dtmf
, guint8 volume
,
2501 PurpleMediaBackendFs2Session
*session
;
2504 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2506 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2507 if (session
== NULL
)
2510 /* Convert DTMF char into FsDTMFEvent enum */
2512 case '0': event
= FS_DTMF_EVENT_0
; break;
2513 case '1': event
= FS_DTMF_EVENT_1
; break;
2514 case '2': event
= FS_DTMF_EVENT_2
; break;
2515 case '3': event
= FS_DTMF_EVENT_3
; break;
2516 case '4': event
= FS_DTMF_EVENT_4
; break;
2517 case '5': event
= FS_DTMF_EVENT_5
; break;
2518 case '6': event
= FS_DTMF_EVENT_6
; break;
2519 case '7': event
= FS_DTMF_EVENT_7
; break;
2520 case '8': event
= FS_DTMF_EVENT_8
; break;
2521 case '9': event
= FS_DTMF_EVENT_9
; break;
2522 case '*': event
= FS_DTMF_EVENT_STAR
; break;
2523 case '#': event
= FS_DTMF_EVENT_POUND
; break;
2524 case 'A': event
= FS_DTMF_EVENT_A
; break;
2525 case 'B': event
= FS_DTMF_EVENT_B
; break;
2526 case 'C': event
= FS_DTMF_EVENT_C
; break;
2527 case 'D': event
= FS_DTMF_EVENT_D
; break;
2532 if (!fs_session_start_telephony_event(session
->session
,
2537 if (duration
<= 50) {
2538 fs_session_stop_telephony_event(session
->session
);
2540 g_timeout_add(duration
, send_dtmf_callback
,
2548 purple_media_backend_fs2_get_type(void)
2555 purple_media_backend_fs2_get_src(PurpleMediaBackendFs2
*self
,
2556 const gchar
*sess_id
)
2559 PurpleMediaBackendFs2Session
*session
= get_session(self
, sess_id
);
2560 return session
!= NULL
? session
->src
: NULL
;
2567 purple_media_backend_fs2_get_tee(PurpleMediaBackendFs2
*self
,
2568 const gchar
*sess_id
, const gchar
*who
)
2571 if (sess_id
!= NULL
&& who
== NULL
) {
2572 PurpleMediaBackendFs2Session
*session
=
2573 get_session(self
, sess_id
);
2574 return (session
!= NULL
) ? session
->tee
: NULL
;
2575 } else if (sess_id
!= NULL
&& who
!= NULL
) {
2576 PurpleMediaBackendFs2Stream
*stream
=
2577 get_stream(self
, sess_id
, who
);
2578 return (stream
!= NULL
) ? stream
->tee
: NULL
;
2582 g_return_val_if_reached(NULL
);
2586 purple_media_backend_fs2_set_input_volume(PurpleMediaBackendFs2
*self
,
2587 const gchar
*sess_id
, double level
)
2590 PurpleMediaBackendFs2Private
*priv
;
2593 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2595 priv
= purple_media_backend_fs2_get_instance_private(self
);
2597 purple_prefs_set_int("/purple/media/audio/volume/input", level
);
2599 if (sess_id
== NULL
)
2600 sessions
= g_hash_table_get_values(priv
->sessions
);
2602 sessions
= g_list_append(NULL
, get_session(self
, sess_id
));
2604 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
2605 PurpleMediaBackendFs2Session
*session
= sessions
->data
;
2607 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
2608 gchar
*name
= g_strdup_printf("volume_%s",
2610 GstElement
*volume
= gst_bin_get_by_name(
2611 GST_BIN(priv
->confbin
), name
);
2613 g_object_set(volume
, "volume", level
/10.0, NULL
);
2620 purple_media_backend_fs2_set_output_volume(PurpleMediaBackendFs2
*self
,
2621 const gchar
*sess_id
, const gchar
*who
, double level
)
2626 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2628 purple_prefs_set_int("/purple/media/audio/volume/output", level
);
2630 streams
= get_streams(self
, sess_id
, who
);
2632 for (; streams
; streams
= g_list_delete_link(streams
, streams
)) {
2633 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
2635 if (stream
->session
->type
& PURPLE_MEDIA_RECV_AUDIO
2636 && GST_IS_ELEMENT(stream
->volume
)) {
2637 g_object_set(stream
->volume
, "volume",
2646 purple_media_backend_fs2_set_send_rtcp_mux(PurpleMediaBackend
*self
,
2647 const gchar
*sess_id
, const gchar
*participant
,
2648 gboolean send_rtcp_mux
)
2650 PurpleMediaBackendFs2Stream
*stream
;
2652 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2653 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2654 sess_id
, participant
);
2656 if (stream
!= NULL
&&
2657 g_object_class_find_property (G_OBJECT_GET_CLASS (stream
->stream
),
2658 "send-rtcp-mux") != NULL
) {
2659 g_object_set (stream
->stream
, "send-rtcp-mux", send_rtcp_mux
, NULL
);