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 for (it
= features
; it
; it
= it
->next
) {
502 gst_plugin_feature_set_rank((GstPluginFeature
*)it
->data
,
505 gst_plugin_feature_list_free(features
);
509 purple_media_backend_iface_init(PurpleMediaBackendInterface
*iface
)
511 iface
->add_stream
= purple_media_backend_fs2_add_stream
;
512 iface
->add_remote_candidates
=
513 purple_media_backend_fs2_add_remote_candidates
;
514 iface
->codecs_ready
= purple_media_backend_fs2_codecs_ready
;
515 iface
->get_codecs
= purple_media_backend_fs2_get_codecs
;
516 iface
->get_local_candidates
=
517 purple_media_backend_fs2_get_local_candidates
;
518 iface
->set_remote_codecs
= purple_media_backend_fs2_set_remote_codecs
;
519 iface
->set_send_codec
= purple_media_backend_fs2_set_send_codec
;
520 iface
->set_encryption_parameters
=
521 purple_media_backend_fs2_set_encryption_parameters
;
522 iface
->set_decryption_parameters
=
523 purple_media_backend_fs2_set_decryption_parameters
;
524 iface
->set_params
= purple_media_backend_fs2_set_params
;
525 iface
->get_available_params
= purple_media_backend_fs2_get_available_params
;
526 iface
->send_dtmf
= purple_media_backend_fs2_send_dtmf
;
527 iface
->set_send_rtcp_mux
= purple_media_backend_fs2_set_send_rtcp_mux
;
531 session_type_to_fs_media_type(PurpleMediaSessionType type
)
533 if (type
& PURPLE_MEDIA_AUDIO
)
534 return FS_MEDIA_TYPE_AUDIO
;
535 else if (type
& PURPLE_MEDIA_VIDEO
)
536 return FS_MEDIA_TYPE_VIDEO
;
537 #ifdef HAVE_MEDIA_APPLICATION
538 else if (type
& PURPLE_MEDIA_APPLICATION
)
539 return FS_MEDIA_TYPE_APPLICATION
;
545 static FsStreamDirection
546 session_type_to_fs_stream_direction(PurpleMediaSessionType type
)
548 if ((type
& PURPLE_MEDIA_AUDIO
) == PURPLE_MEDIA_AUDIO
||
549 (type
& PURPLE_MEDIA_VIDEO
) == PURPLE_MEDIA_VIDEO
)
550 return FS_DIRECTION_BOTH
;
551 else if ((type
& PURPLE_MEDIA_SEND_AUDIO
) ||
552 (type
& PURPLE_MEDIA_SEND_VIDEO
))
553 return FS_DIRECTION_SEND
;
554 else if ((type
& PURPLE_MEDIA_RECV_AUDIO
) ||
555 (type
& PURPLE_MEDIA_RECV_VIDEO
))
556 return FS_DIRECTION_RECV
;
557 #ifdef HAVE_MEDIA_APPLICATION
558 else if ((type
& PURPLE_MEDIA_APPLICATION
) == PURPLE_MEDIA_APPLICATION
)
559 return FS_DIRECTION_BOTH
;
560 else if (type
& PURPLE_MEDIA_SEND_APPLICATION
)
561 return FS_DIRECTION_SEND
;
562 else if (type
& PURPLE_MEDIA_RECV_APPLICATION
)
563 return FS_DIRECTION_RECV
;
566 return FS_DIRECTION_NONE
;
569 static PurpleMediaSessionType
570 session_type_from_fs(FsMediaType type
, FsStreamDirection direction
)
572 PurpleMediaSessionType result
= PURPLE_MEDIA_NONE
;
573 if (type
== FS_MEDIA_TYPE_AUDIO
) {
574 if (direction
& FS_DIRECTION_SEND
)
575 result
|= PURPLE_MEDIA_SEND_AUDIO
;
576 if (direction
& FS_DIRECTION_RECV
)
577 result
|= PURPLE_MEDIA_RECV_AUDIO
;
578 } else if (type
== FS_MEDIA_TYPE_VIDEO
) {
579 if (direction
& FS_DIRECTION_SEND
)
580 result
|= PURPLE_MEDIA_SEND_VIDEO
;
581 if (direction
& FS_DIRECTION_RECV
)
582 result
|= PURPLE_MEDIA_RECV_VIDEO
;
583 #ifdef HAVE_MEDIA_APPLICATION
584 } else if (type
== FS_MEDIA_TYPE_APPLICATION
) {
585 if (direction
& FS_DIRECTION_SEND
)
586 result
|= PURPLE_MEDIA_SEND_APPLICATION
;
587 if (direction
& FS_DIRECTION_RECV
)
588 result
|= PURPLE_MEDIA_RECV_APPLICATION
;
595 candidate_to_fs(PurpleMediaCandidate
*candidate
)
597 FsCandidate
*fscandidate
;
604 PurpleMediaNetworkProtocol proto
;
606 PurpleMediaCandidateType type
;
611 if (candidate
== NULL
)
614 g_object_get(G_OBJECT(candidate
),
615 "foundation", &foundation
,
616 "component-id", &component_id
,
620 "base-port", &base_port
,
622 "priority", &priority
,
624 "username", &username
,
625 "password", &password
,
629 fscandidate
= fs_candidate_new(foundation
,
630 component_id
, purple_media_candidate_type_to_fs(type
),
631 purple_media_network_protocol_to_fs(proto
), ip
, port
);
633 fscandidate
->base_ip
= base_ip
;
634 fscandidate
->base_port
= base_port
;
635 fscandidate
->priority
= priority
;
636 fscandidate
->username
= username
;
637 fscandidate
->password
= password
;
638 fscandidate
->ttl
= ttl
;
646 candidate_list_to_fs(GList
*candidates
)
648 GList
*new_list
= NULL
;
650 for (; candidates
; candidates
= g_list_next(candidates
)) {
651 new_list
= g_list_prepend(new_list
,
652 candidate_to_fs(candidates
->data
));
655 new_list
= g_list_reverse(new_list
);
659 static PurpleMediaCandidate
*
660 candidate_from_fs(FsCandidate
*fscandidate
)
662 PurpleMediaCandidate
*candidate
;
664 if (fscandidate
== NULL
)
667 candidate
= purple_media_candidate_new(fscandidate
->foundation
,
668 fscandidate
->component_id
,
669 purple_media_candidate_type_from_fs(fscandidate
->type
),
670 purple_media_network_protocol_from_fs(fscandidate
->proto
),
671 fscandidate
->ip
, fscandidate
->port
);
672 g_object_set(candidate
,
673 "base-ip", fscandidate
->base_ip
,
674 "base-port", fscandidate
->base_port
,
675 "priority", fscandidate
->priority
,
676 "username", fscandidate
->username
,
677 "password", fscandidate
->password
,
678 "ttl", fscandidate
->ttl
, NULL
);
683 candidate_list_from_fs(GList
*candidates
)
685 GList
*new_list
= NULL
;
687 for (; candidates
; candidates
= g_list_next(candidates
)) {
688 new_list
= g_list_prepend(new_list
,
689 candidate_from_fs(candidates
->data
));
692 new_list
= g_list_reverse(new_list
);
697 codec_to_fs(const PurpleMediaCodec
*codec
)
702 PurpleMediaSessionType media_type
;
710 g_object_get(G_OBJECT(codec
),
712 "encoding-name", &encoding_name
,
713 "media-type", &media_type
,
714 "clock-rate", &clock_rate
,
715 "channels", &channels
,
716 "optional-params", &iter
,
719 new_codec
= fs_codec_new(id
, encoding_name
,
720 session_type_to_fs_media_type(media_type
),
722 new_codec
->channels
= channels
;
724 for (; iter
; iter
= g_list_next(iter
)) {
725 PurpleKeyValuePair
*param
= (PurpleKeyValuePair
*)iter
->data
;
726 fs_codec_add_optional_parameter(new_codec
,
727 param
->key
, param
->value
);
730 g_free(encoding_name
);
734 static PurpleMediaCodec
*
735 codec_from_fs(const FsCodec
*codec
)
737 PurpleMediaCodec
*new_codec
;
743 new_codec
= purple_media_codec_new(codec
->id
, codec
->encoding_name
,
744 session_type_from_fs(codec
->media_type
,
745 FS_DIRECTION_BOTH
), codec
->clock_rate
);
746 g_object_set(new_codec
, "channels", codec
->channels
, NULL
);
748 for (iter
= codec
->optional_params
; iter
; iter
= g_list_next(iter
)) {
749 FsCodecParameter
*param
= (FsCodecParameter
*)iter
->data
;
750 purple_media_codec_add_optional_parameter(new_codec
,
751 param
->name
, param
->value
);
758 codec_list_from_fs(GList
*codecs
)
760 GList
*new_list
= NULL
;
762 for (; codecs
; codecs
= g_list_next(codecs
)) {
763 new_list
= g_list_prepend(new_list
,
764 codec_from_fs(codecs
->data
));
767 new_list
= g_list_reverse(new_list
);
772 codec_list_to_fs(GList
*codecs
)
774 GList
*new_list
= NULL
;
776 for (; codecs
; codecs
= g_list_next(codecs
)) {
777 new_list
= g_list_prepend(new_list
,
778 codec_to_fs(codecs
->data
));
781 new_list
= g_list_reverse(new_list
);
785 static PurpleMediaBackendFs2Session
*
786 get_session(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
)
788 PurpleMediaBackendFs2Private
*priv
;
789 PurpleMediaBackendFs2Session
*session
= NULL
;
791 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
793 priv
= purple_media_backend_fs2_get_instance_private(self
);
795 if (priv
->sessions
!= NULL
)
796 session
= g_hash_table_lookup(priv
->sessions
, sess_id
);
801 static FsParticipant
*
802 get_participant(PurpleMediaBackendFs2
*self
, const gchar
*name
)
804 PurpleMediaBackendFs2Private
*priv
;
805 FsParticipant
*participant
= NULL
;
807 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
809 priv
= purple_media_backend_fs2_get_instance_private(self
);
811 if (priv
->participants
!= NULL
)
812 participant
= g_hash_table_lookup(priv
->participants
, name
);
817 static PurpleMediaBackendFs2Stream
*
818 get_stream(PurpleMediaBackendFs2
*self
,
819 const gchar
*sess_id
, const gchar
*name
)
821 PurpleMediaBackendFs2Private
*priv
;
824 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
826 priv
= purple_media_backend_fs2_get_instance_private(self
);
827 streams
= priv
->streams
;
829 for (; streams
; streams
= g_list_next(streams
)) {
830 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
831 if (purple_strequal(stream
->session
->id
, sess_id
) &&
832 purple_strequal(stream
->participant
, name
))
840 get_streams(PurpleMediaBackendFs2
*self
,
841 const gchar
*sess_id
, const gchar
*name
)
843 PurpleMediaBackendFs2Private
*priv
;
844 GList
*streams
, *ret
= NULL
;
846 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
848 priv
= purple_media_backend_fs2_get_instance_private(self
);
849 streams
= priv
->streams
;
851 for (; streams
; streams
= g_list_next(streams
)) {
852 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
854 if (sess_id
!= NULL
&& !purple_strequal(stream
->session
->id
, sess_id
))
856 else if (name
!= NULL
&& !purple_strequal(stream
->participant
, name
))
859 ret
= g_list_prepend(ret
, stream
);
862 ret
= g_list_reverse(ret
);
866 static PurpleMediaBackendFs2Session
*
867 get_session_from_fs_stream(PurpleMediaBackendFs2
*self
, FsStream
*stream
)
869 PurpleMediaBackendFs2Private
*priv
=
870 purple_media_backend_fs2_get_instance_private(self
);
871 FsSession
*fssession
;
874 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
875 g_return_val_if_fail(FS_IS_STREAM(stream
), NULL
);
877 g_object_get(stream
, "session", &fssession
, NULL
);
879 values
= g_hash_table_get_values(priv
->sessions
);
881 for (; values
; values
= g_list_delete_link(values
, values
)) {
882 PurpleMediaBackendFs2Session
*session
= values
->data
;
884 if (session
->session
== fssession
) {
886 g_object_unref(fssession
);
891 g_object_unref(fssession
);
896 gst_msg_db_to_percent(GstMessage
*msg
, gchar
*value_name
)
903 list
= gst_structure_get_value(gst_message_get_structure(msg
), value_name
);
904 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
905 value
= g_value_array_get_nth(g_value_get_boxed(list
), 0);
906 G_GNUC_END_IGNORE_DEPRECATIONS
907 value_db
= g_value_get_double(value
);
908 percent
= pow(10, value_db
/ 20);
909 return (percent
> 1.0) ? 1.0 : percent
;
913 purple_media_error_fs(PurpleMedia
*media
, const gchar
*error
,
914 const GstStructure
*fs_error
)
916 const gchar
*error_msg
= gst_structure_get_string(fs_error
, "error-msg");
918 purple_media_error(media
, "%s%s%s", error
,
919 error_msg
? _("\n\nMessage from Farstream: ") : "",
920 error_msg
? error_msg
: "");
924 gst_handle_message_element(GstBus
*bus
, GstMessage
*msg
,
925 PurpleMediaBackendFs2
*self
)
927 PurpleMediaBackendFs2Private
*priv
=
928 purple_media_backend_fs2_get_instance_private(self
);
929 GstElement
*src
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
930 static guint level_id
= 0;
931 const GstStructure
*structure
= gst_message_get_structure(msg
);
934 level_id
= g_signal_lookup("level", PURPLE_TYPE_MEDIA
);
936 if (gst_structure_has_name(structure
, "level")) {
937 GstElement
*src
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
939 gchar
*participant
= NULL
;
940 PurpleMediaBackendFs2Session
*session
= NULL
;
943 if (!PURPLE_IS_MEDIA(priv
->media
) ||
944 GST_ELEMENT_PARENT(src
) != priv
->confbin
)
947 name
= gst_element_get_name(src
);
949 if (!strncmp(name
, "sendlevel_", 10)) {
950 session
= get_session(self
, name
+10);
951 if (priv
->silence_threshold
> 0) {
952 percent
= gst_msg_db_to_percent(msg
, "decay");
953 g_object_set(session
->srcvalve
,
954 "drop", (percent
< priv
->silence_threshold
), NULL
);
960 if (!g_signal_has_handler_pending(priv
->media
, level_id
, 0, FALSE
))
964 GList
*iter
= priv
->streams
;
965 PurpleMediaBackendFs2Stream
*stream
;
966 for (; iter
; iter
= g_list_next(iter
)) {
968 if (stream
->level
== src
) {
969 session
= stream
->session
;
970 participant
= stream
->participant
;
979 percent
= gst_msg_db_to_percent(msg
, "rms");
981 g_signal_emit(priv
->media
, level_id
, 0,
982 session
->id
, participant
, percent
);
986 if (!FS_IS_CONFERENCE(src
) || !PURPLE_IS_MEDIA_BACKEND(self
) ||
987 priv
->conference
!= FS_CONFERENCE(src
))
990 if (gst_structure_has_name(structure
, "farstream-error")) {
992 gboolean error_emitted
= FALSE
;
993 gst_structure_get_enum(structure
, "error-no",
994 FS_TYPE_ERROR
, (gint
*)&error_no
);
996 case FS_ERROR_CONSTRUCTION
:
997 purple_media_error_fs(priv
->media
,
998 _("Error initializing the call. "
999 "This probably denotes problem in "
1000 "installation of GStreamer or Farstream."),
1002 error_emitted
= TRUE
;
1004 case FS_ERROR_NETWORK
:
1005 purple_media_error_fs(priv
->media
, _("Network error."),
1007 error_emitted
= TRUE
;
1008 purple_media_end(priv
->media
, NULL
, NULL
);
1010 case FS_ERROR_NEGOTIATION_FAILED
:
1011 purple_media_error_fs(priv
->media
,
1012 _("Codec negotiation failed. "
1013 "This problem might be resolved by installing "
1014 "more GStreamer codecs."),
1016 error_emitted
= TRUE
;
1017 purple_media_end(priv
->media
, NULL
, NULL
);
1019 case FS_ERROR_NO_CODECS
:
1020 purple_media_error(priv
->media
,
1021 _("No codecs found. "
1022 "Install some GStreamer codecs found "
1023 "in GStreamer plugins packages."));
1024 error_emitted
= TRUE
;
1025 purple_media_end(priv
->media
, NULL
, NULL
);
1028 purple_debug_error("backend-fs2",
1029 "farstream-error: %i: %s\n",
1031 gst_structure_get_string(structure
, "error-msg"));
1035 if (FS_ERROR_IS_FATAL(error_no
)) {
1037 purple_media_error(priv
->media
,
1038 _("A non-recoverable Farstream error has occurred."));
1039 purple_media_end(priv
->media
, NULL
, NULL
);
1041 } else if (gst_structure_has_name(structure
,
1042 "farstream-new-local-candidate")) {
1043 const GValue
*value
;
1045 FsCandidate
*local_candidate
;
1046 PurpleMediaCandidate
*candidate
;
1047 FsParticipant
*participant
;
1048 PurpleMediaBackendFs2Session
*session
;
1049 PurpleMediaBackendFs2Stream
*media_stream
;
1052 value
= gst_structure_get_value(structure
, "stream");
1053 stream
= g_value_get_object(value
);
1054 value
= gst_structure_get_value(structure
, "candidate");
1055 local_candidate
= g_value_get_boxed(value
);
1057 session
= get_session_from_fs_stream(self
, stream
);
1059 purple_debug_info("backend-fs2",
1060 "got new local candidate: %s\n",
1061 local_candidate
->foundation
);
1063 g_object_get(stream
, "participant", &participant
, NULL
);
1064 name
= g_object_get_data(G_OBJECT(participant
), "purple-name");
1066 media_stream
= get_stream(self
, session
->id
, name
);
1067 media_stream
->local_candidates
= g_list_append(
1068 media_stream
->local_candidates
,
1069 fs_candidate_copy(local_candidate
));
1071 candidate
= candidate_from_fs(local_candidate
);
1072 g_signal_emit_by_name(self
, "new-candidate",
1073 session
->id
, name
, candidate
);
1074 g_object_unref(candidate
);
1075 g_object_unref(participant
);
1076 } else if (gst_structure_has_name(structure
,
1077 "farstream-local-candidates-prepared")) {
1078 const GValue
*value
;
1080 FsParticipant
*participant
;
1081 PurpleMediaBackendFs2Session
*session
;
1083 value
= gst_structure_get_value(structure
, "stream");
1084 stream
= g_value_get_object(value
);
1085 session
= get_session_from_fs_stream(self
, stream
);
1087 g_object_get(stream
, "participant", &participant
, NULL
);
1089 g_signal_emit_by_name(self
, "candidates-prepared",
1091 g_object_get_data(G_OBJECT(participant
), "purple-name"));
1093 g_object_unref(participant
);
1094 } else if (gst_structure_has_name(structure
,
1095 "farstream-new-active-candidate-pair")) {
1096 const GValue
*value
;
1098 FsCandidate
*local_candidate
;
1099 FsCandidate
*remote_candidate
;
1100 FsParticipant
*participant
;
1101 PurpleMediaBackendFs2Session
*session
;
1102 PurpleMediaCandidate
*lcandidate
, *rcandidate
;
1104 value
= gst_structure_get_value(structure
, "stream");
1105 stream
= g_value_get_object(value
);
1106 value
= gst_structure_get_value(structure
, "local-candidate");
1107 local_candidate
= g_value_get_boxed(value
);
1108 value
= gst_structure_get_value(structure
, "remote-candidate");
1109 remote_candidate
= g_value_get_boxed(value
);
1111 g_object_get(stream
, "participant", &participant
, NULL
);
1113 session
= get_session_from_fs_stream(self
, stream
);
1115 lcandidate
= candidate_from_fs(local_candidate
);
1116 rcandidate
= candidate_from_fs(remote_candidate
);
1118 g_signal_emit_by_name(self
, "active-candidate-pair",
1120 g_object_get_data(G_OBJECT(participant
), "purple-name"),
1121 lcandidate
, rcandidate
);
1123 g_object_unref(participant
);
1124 g_object_unref(lcandidate
);
1125 g_object_unref(rcandidate
);
1126 } else if (gst_structure_has_name(structure
,
1127 "farstream-recv-codecs-changed")) {
1128 const GValue
*value
;
1132 value
= gst_structure_get_value(structure
, "codecs");
1133 codecs
= g_value_get_boxed(value
);
1134 codec
= codecs
->data
;
1136 purple_debug_info("backend-fs2",
1137 "farstream-recv-codecs-changed: %s\n",
1138 codec
->encoding_name
);
1139 } else if (gst_structure_has_name(structure
,
1140 "farstream-component-state-changed")) {
1141 const GValue
*value
;
1142 FsStreamState fsstate
;
1146 value
= gst_structure_get_value(structure
, "state");
1147 fsstate
= g_value_get_enum(value
);
1148 value
= gst_structure_get_value(structure
, "component");
1149 component
= g_value_get_uint(value
);
1152 case FS_STREAM_STATE_FAILED
:
1155 case FS_STREAM_STATE_DISCONNECTED
:
1156 state
= "DISCONNECTED";
1158 case FS_STREAM_STATE_GATHERING
:
1159 state
= "GATHERING";
1161 case FS_STREAM_STATE_CONNECTING
:
1162 state
= "CONNECTING";
1164 case FS_STREAM_STATE_CONNECTED
:
1165 state
= "CONNECTED";
1167 case FS_STREAM_STATE_READY
:
1175 purple_debug_info("backend-fs2",
1176 "farstream-component-state-changed: "
1177 "component: %u state: %s\n",
1179 } else if (gst_structure_has_name(structure
,
1180 "farstream-send-codec-changed")) {
1181 const GValue
*value
;
1185 value
= gst_structure_get_value(structure
, "codec");
1186 codec
= g_value_get_boxed(value
);
1187 codec_str
= fs_codec_to_string(codec
);
1189 purple_debug_info("backend-fs2",
1190 "farstream-send-codec-changed: codec: %s\n",
1194 } else if (gst_structure_has_name(structure
,
1195 "farstream-codecs-changed")) {
1196 const GValue
*value
;
1197 FsSession
*fssession
;
1200 value
= gst_structure_get_value(structure
, "session");
1201 fssession
= g_value_get_object(value
);
1202 sessions
= g_hash_table_get_values(priv
->sessions
);
1204 for (; sessions
; sessions
=
1205 g_list_delete_link(sessions
, sessions
)) {
1206 PurpleMediaBackendFs2Session
*session
= sessions
->data
;
1209 if (session
->session
!= fssession
)
1212 session_id
= g_strdup(session
->id
);
1213 g_signal_emit_by_name(self
, "codecs-changed",
1216 g_list_free(sessions
);
1223 gst_handle_message_error(GstBus
*bus
, GstMessage
*msg
,
1224 PurpleMediaBackendFs2
*self
)
1226 PurpleMediaBackendFs2Private
*priv
=
1227 purple_media_backend_fs2_get_instance_private(self
);
1228 GstElement
*element
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
1229 GstElement
*lastElement
= NULL
;
1232 GError
*error
= NULL
;
1233 gchar
*debug_msg
= NULL
;
1235 gst_message_parse_error(msg
, &error
, &debug_msg
);
1236 purple_debug_error("backend-fs2", "gst error %s\ndebugging: %s\n",
1237 error
->message
, debug_msg
);
1239 g_error_free(error
);
1242 while (element
&& !GST_IS_PIPELINE(element
)) {
1243 if (element
== priv
->confbin
)
1246 lastElement
= element
;
1247 element
= GST_ELEMENT_PARENT(element
);
1250 if (!element
|| !GST_IS_PIPELINE(element
))
1253 sessions
= purple_media_get_session_ids(priv
->media
);
1255 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
1256 if (purple_media_get_src(priv
->media
, sessions
->data
)
1260 if (purple_media_get_session_type(priv
->media
, sessions
->data
)
1261 & PURPLE_MEDIA_AUDIO
)
1262 purple_media_error(priv
->media
,
1263 _("Error with your microphone"));
1264 else if (purple_media_get_session_type(priv
->media
,
1265 sessions
->data
) & PURPLE_MEDIA_VIDEO
)
1266 purple_media_error(priv
->media
,
1267 _("Error with your webcam"));
1272 g_list_free(sessions
);
1274 purple_media_error(priv
->media
, _("Conference error"));
1275 purple_media_end(priv
->media
, NULL
, NULL
);
1279 gst_bus_cb(GstBus
*bus
, GstMessage
*msg
, PurpleMediaBackendFs2
*self
)
1281 switch(GST_MESSAGE_TYPE(msg
)) {
1282 case GST_MESSAGE_ELEMENT
:
1283 gst_handle_message_element(bus
, msg
, self
);
1285 case GST_MESSAGE_ERROR
:
1286 gst_handle_message_error(bus
, msg
, self
);
1296 remove_element(GstElement
*element
)
1299 gst_element_set_locked_state(element
, TRUE
);
1300 gst_element_set_state(element
, GST_STATE_NULL
);
1301 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(element
)), element
);
1306 state_changed_cb(PurpleMedia
*media
, PurpleMediaState state
,
1307 gchar
*sid
, gchar
*name
, PurpleMediaBackendFs2
*self
)
1309 if (state
== PURPLE_MEDIA_STATE_END
) {
1310 PurpleMediaBackendFs2Private
*priv
=
1311 purple_media_backend_fs2_get_instance_private(
1315 PurpleMediaBackendFs2Stream
*stream
= get_stream(self
, sid
, name
);
1316 gst_object_unref(stream
->stream
);
1318 priv
->streams
= g_list_remove(priv
->streams
, stream
);
1320 remove_element(stream
->src
);
1321 remove_element(stream
->tee
);
1322 remove_element(stream
->volume
);
1323 remove_element(stream
->level
);
1324 remove_element(stream
->fakesink
);
1325 remove_element(stream
->queue
);
1327 free_stream(stream
);
1328 } else if (sid
&& !name
) {
1329 PurpleMediaBackendFs2Session
*session
= get_session(self
, sid
);
1332 g_object_get(session
->session
, "sink-pad", &pad
, NULL
);
1333 gst_pad_unlink(GST_PAD_PEER(pad
), pad
);
1334 gst_object_unref(pad
);
1336 gst_object_unref(session
->session
);
1337 g_hash_table_remove(priv
->sessions
, session
->id
);
1339 pad
= gst_pad_get_peer(session
->srcpad
);
1340 gst_element_remove_pad(GST_ELEMENT_PARENT(pad
), pad
);
1341 gst_object_unref(pad
);
1342 gst_object_unref(session
->srcpad
);
1344 remove_element(session
->srcvalve
);
1345 remove_element(session
->tee
);
1347 free_session(session
);
1350 purple_media_manager_remove_output_windows(
1351 purple_media_get_manager(media
), media
, sid
, name
);
1356 stream_info_cb(PurpleMedia
*media
, PurpleMediaInfoType type
,
1357 gchar
*sid
, gchar
*name
, gboolean local
,
1358 PurpleMediaBackendFs2
*self
)
1360 if (type
== PURPLE_MEDIA_INFO_ACCEPT
&& sid
!= NULL
&& name
!= NULL
) {
1361 PurpleMediaBackendFs2Stream
*stream
=
1362 get_stream(self
, sid
, name
);
1365 g_object_set(G_OBJECT(stream
->stream
), "direction",
1366 session_type_to_fs_stream_direction(
1367 stream
->session
->type
), NULL
);
1369 if (stream
->remote_candidates
== NULL
||
1370 purple_media_is_initiator(media
, sid
, name
))
1373 if (stream
->supports_add
)
1374 fs_stream_add_remote_candidates(stream
->stream
,
1375 stream
->remote_candidates
, &err
);
1377 fs_stream_force_remote_candidates(stream
->stream
,
1378 stream
->remote_candidates
, &err
);
1383 purple_debug_error("backend-fs2", "Error adding "
1384 "remote candidates: %s\n",
1387 } else if (local
== TRUE
&& (type
== PURPLE_MEDIA_INFO_MUTE
||
1388 type
== PURPLE_MEDIA_INFO_UNMUTE
)) {
1389 PurpleMediaBackendFs2Private
*priv
=
1390 purple_media_backend_fs2_get_instance_private(
1392 gboolean active
= (type
== PURPLE_MEDIA_INFO_MUTE
);
1396 sessions
= g_hash_table_get_values(priv
->sessions
);
1398 sessions
= g_list_prepend(NULL
,
1399 get_session(self
, sid
));
1401 purple_debug_info("media", "Turning mute %s\n",
1402 active
? "on" : "off");
1404 for (; sessions
; sessions
= g_list_delete_link(
1405 sessions
, sessions
)) {
1406 PurpleMediaBackendFs2Session
*session
=
1409 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
1410 gchar
*name
= g_strdup_printf("volume_%s",
1412 GstElement
*volume
= gst_bin_get_by_name(
1413 GST_BIN(priv
->confbin
), name
);
1415 g_object_set(volume
, "mute", active
, NULL
);
1418 } else if (local
== TRUE
&& (type
== PURPLE_MEDIA_INFO_HOLD
||
1419 type
== PURPLE_MEDIA_INFO_UNHOLD
)) {
1420 gboolean active
= (type
== PURPLE_MEDIA_INFO_HOLD
);
1421 GList
*streams
= get_streams(self
, sid
, name
);
1422 for (; streams
; streams
=
1423 g_list_delete_link(streams
, streams
)) {
1424 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
1425 if (stream
->session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
1426 g_object_set(stream
->stream
, "direction",
1427 session_type_to_fs_stream_direction(
1428 stream
->session
->type
& ((active
) ?
1429 ~PURPLE_MEDIA_SEND_AUDIO
:
1430 PURPLE_MEDIA_AUDIO
)), NULL
);
1433 } else if (local
== TRUE
&& (type
== PURPLE_MEDIA_INFO_PAUSE
||
1434 type
== PURPLE_MEDIA_INFO_UNPAUSE
)) {
1435 gboolean active
= (type
== PURPLE_MEDIA_INFO_PAUSE
);
1436 GList
*streams
= get_streams(self
, sid
, name
);
1437 for (; streams
; streams
=
1438 g_list_delete_link(streams
, streams
)) {
1439 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
1440 if (stream
->session
->type
& PURPLE_MEDIA_SEND_VIDEO
) {
1441 g_object_set(stream
->stream
, "direction",
1442 session_type_to_fs_stream_direction(
1443 stream
->session
->type
& ((active
) ?
1444 ~PURPLE_MEDIA_SEND_VIDEO
:
1445 PURPLE_MEDIA_VIDEO
)), NULL
);
1452 init_conference(PurpleMediaBackendFs2
*self
)
1454 PurpleMediaBackendFs2Private
*priv
=
1455 purple_media_backend_fs2_get_instance_private(self
);
1456 GstElement
*pipeline
;
1459 GKeyFile
*default_props
;
1461 priv
->conference
= FS_CONFERENCE(
1462 gst_element_factory_make(priv
->conference_type
, NULL
));
1464 if (priv
->conference
== NULL
) {
1465 purple_debug_error("backend-fs2", "Conference == NULL\n");
1469 if (purple_account_get_silence_suppression(
1470 purple_media_get_account(priv
->media
)))
1471 priv
->silence_threshold
= purple_prefs_get_int(
1472 "/purple/media/audio/silence_threshold") / 100.0;
1474 priv
->silence_threshold
= 0;
1476 pipeline
= purple_media_manager_get_pipeline(
1477 purple_media_get_manager(priv
->media
));
1479 if (pipeline
== NULL
) {
1480 purple_debug_error("backend-fs2",
1481 "Couldn't retrieve pipeline.\n");
1485 name
= g_strdup_printf("conf_%p", priv
->conference
);
1486 priv
->confbin
= gst_bin_new(name
);
1487 if (priv
->confbin
== NULL
) {
1488 purple_debug_error("backend-fs2",
1489 "Couldn't create confbin.\n");
1495 bus
= gst_pipeline_get_bus(GST_PIPELINE(pipeline
));
1497 purple_debug_error("backend-fs2",
1498 "Couldn't get the pipeline's bus.\n");
1502 default_props
= fs_utils_get_default_element_properties(GST_ELEMENT(priv
->conference
));
1503 if (default_props
!= NULL
) {
1504 priv
->notifier
= fs_element_added_notifier_new();
1505 fs_element_added_notifier_add(priv
->notifier
,
1506 GST_BIN(priv
->confbin
));
1507 fs_element_added_notifier_set_properties_from_keyfile(priv
->notifier
, default_props
);
1510 g_signal_connect(G_OBJECT(bus
), "message",
1511 G_CALLBACK(gst_bus_cb
), self
);
1512 gst_object_unref(bus
);
1514 if (!gst_bin_add(GST_BIN(pipeline
),
1515 GST_ELEMENT(priv
->confbin
))) {
1516 purple_debug_error("backend-fs2", "Couldn't add confbin "
1517 "element to the pipeline\n");
1521 if (!gst_bin_add(GST_BIN(priv
->confbin
),
1522 GST_ELEMENT(priv
->conference
))) {
1523 purple_debug_error("backend-fs2", "Couldn't add conference "
1524 "element to the confbin\n");
1528 if (gst_element_set_state(GST_ELEMENT(priv
->confbin
),
1529 GST_STATE_PLAYING
) == GST_STATE_CHANGE_FAILURE
) {
1530 purple_debug_error("backend-fs2",
1531 "Failed to start conference.\n");
1539 create_src(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
,
1540 PurpleMediaSessionType type
)
1542 PurpleMediaBackendFs2Private
*priv
=
1543 purple_media_backend_fs2_get_instance_private(self
);
1544 PurpleMediaBackendFs2Session
*session
;
1545 PurpleMediaSessionType session_type
;
1546 FsMediaType media_type
= session_type_to_fs_media_type(type
);
1547 FsStreamDirection type_direction
=
1548 session_type_to_fs_stream_direction(type
);
1550 GstPad
*sinkpad
, *srcpad
;
1551 GstPad
*ghost
= NULL
;
1553 if ((type_direction
& FS_DIRECTION_SEND
) == 0)
1556 session_type
= session_type_from_fs(
1557 media_type
, FS_DIRECTION_SEND
);
1558 src
= purple_media_manager_get_element(
1559 purple_media_get_manager(priv
->media
),
1560 session_type
, priv
->media
, sess_id
, NULL
);
1562 if (!GST_IS_ELEMENT(src
)) {
1563 purple_debug_error("backend-fs2",
1564 "Error creating src for session %s\n",
1569 session
= get_session(self
, sess_id
);
1571 if (session
== NULL
) {
1572 purple_debug_warning("backend-fs2",
1573 "purple_media_set_src: trying to set"
1574 " src on non-existent session\n");
1579 gst_object_unref(session
->src
);
1582 gst_element_set_locked_state(session
->src
, TRUE
);
1584 session
->tee
= gst_element_factory_make("tee", NULL
);
1585 gst_bin_add(GST_BIN(priv
->confbin
), session
->tee
);
1587 /* This supposedly isn't necessary, but it silences some warnings */
1588 if (GST_ELEMENT_PARENT(priv
->confbin
)
1589 == GST_ELEMENT_PARENT(session
->src
)) {
1590 GstPad
*pad
= gst_element_get_static_pad(session
->tee
, "sink");
1591 ghost
= gst_ghost_pad_new(NULL
, pad
);
1592 gst_object_unref(pad
);
1593 gst_pad_set_active(ghost
, TRUE
);
1594 gst_element_add_pad(priv
->confbin
, ghost
);
1597 gst_element_set_state(session
->tee
, GST_STATE_PLAYING
);
1598 gst_element_link(session
->src
, priv
->confbin
);
1600 session
->srcpad
= gst_pad_get_peer(ghost
);
1602 g_object_get(session
->session
, "sink-pad", &sinkpad
, NULL
);
1603 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
1604 gchar
*name
= g_strdup_printf("volume_%s", session
->id
);
1606 GstElement
*volume
= gst_element_factory_make("volume", name
);
1607 double input_volume
= purple_prefs_get_int(
1608 "/purple/media/audio/volume/input")/10.0;
1610 name
= g_strdup_printf("sendlevel_%s", session
->id
);
1611 level
= gst_element_factory_make("level", name
);
1613 session
->srcvalve
= gst_element_factory_make("valve", NULL
);
1614 gst_bin_add(GST_BIN(priv
->confbin
), volume
);
1615 gst_bin_add(GST_BIN(priv
->confbin
), level
);
1616 gst_bin_add(GST_BIN(priv
->confbin
), session
->srcvalve
);
1617 gst_element_set_state(level
, GST_STATE_PLAYING
);
1618 gst_element_set_state(volume
, GST_STATE_PLAYING
);
1619 gst_element_set_state(session
->srcvalve
, GST_STATE_PLAYING
);
1620 gst_element_link(level
, session
->srcvalve
);
1621 gst_element_link(volume
, level
);
1622 gst_element_link(session
->tee
, volume
);
1623 srcpad
= gst_element_get_static_pad(session
->srcvalve
, "src");
1624 g_object_set(volume
, "volume", input_volume
, NULL
);
1626 srcpad
= gst_element_get_request_pad(session
->tee
, "src_%u");
1629 purple_debug_info("backend-fs2", "connecting pad: %s\n",
1630 gst_pad_link(srcpad
, sinkpad
) == GST_PAD_LINK_OK
1631 ? "success" : "failure");
1632 gst_element_set_locked_state(session
->src
, FALSE
);
1633 gst_object_unref(session
->src
);
1634 gst_object_unref(sinkpad
);
1636 purple_media_manager_create_output_window(purple_media_get_manager(
1637 priv
->media
), priv
->media
, sess_id
, NULL
);
1639 purple_debug_info("backend-fs2", "create_src: setting source "
1640 "state to GST_STATE_PLAYING - it may hang here on win32\n");
1641 gst_element_set_state(session
->src
, GST_STATE_PLAYING
);
1642 purple_debug_info("backend-fs2", "create_src: state set\n");
1648 create_session(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
,
1649 PurpleMediaSessionType type
, gboolean initiator
,
1650 const gchar
*transmitter
)
1652 PurpleMediaBackendFs2Private
*priv
=
1653 purple_media_backend_fs2_get_instance_private(self
);
1654 PurpleMediaBackendFs2Session
*session
;
1656 GList
*codec_conf
= NULL
;
1657 gchar
*filename
= NULL
;
1659 session
= g_new0(PurpleMediaBackendFs2Session
, 1);
1661 session
->session
= fs_conference_new_session(priv
->conference
,
1662 session_type_to_fs_media_type(type
), &err
);
1664 #ifdef HAVE_MEDIA_APPLICATION
1665 if (type
== PURPLE_MEDIA_APPLICATION
) {
1667 GObject
*rtpsession
= NULL
;
1669 caps
= gst_caps_new_empty_simple ("application/octet-stream");
1670 fs_session_set_allowed_caps (session
->session
, caps
, caps
, NULL
);
1671 gst_caps_unref (caps
);
1672 g_object_get (session
->session
, "internal-session", &rtpsession
, NULL
);
1674 g_object_set (rtpsession
, "probation", 0, NULL
);
1675 g_object_unref (rtpsession
);
1680 purple_media_error(priv
->media
,
1681 _("Error creating session: %s"),
1688 filename
= g_build_filename(purple_config_dir(), "fs-codec.conf", NULL
);
1689 codec_conf
= fs_codec_list_from_keyfile(filename
, &err
);
1693 if (err
->code
== G_KEY_FILE_ERROR_NOT_FOUND
)
1694 purple_debug_info("backend-fs2", "Couldn't read "
1695 "fs-codec.conf: %s\n",
1698 purple_debug_error("backend-fs2", "Error reading "
1699 "fs-codec.conf: %s\n",
1703 purple_debug_info("backend-fs2",
1704 "Loading default codec conf instead\n");
1705 codec_conf
= fs_utils_get_default_codec_preferences(
1706 GST_ELEMENT(priv
->conference
));
1709 fs_session_set_codec_preferences(session
->session
, codec_conf
, NULL
);
1710 fs_codec_list_destroy(codec_conf
);
1713 * Removes a 5-7 second delay before
1714 * receiving the src-pad-added signal.
1715 * Only works for non-multicast FsRtpSessions.
1717 if (!purple_strequal(transmitter
, "multicast"))
1718 g_object_set(G_OBJECT(session
->session
),
1719 "no-rtcp-timeout", 0, NULL
);
1721 session
->id
= g_strdup(sess_id
);
1722 session
->backend
= self
;
1723 session
->type
= type
;
1725 if (!priv
->sessions
) {
1726 purple_debug_info("backend-fs2",
1727 "Creating hash table for sessions\n");
1728 priv
->sessions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1732 g_hash_table_insert(priv
->sessions
, g_strdup(session
->id
), session
);
1734 if (!create_src(self
, sess_id
, type
)) {
1735 purple_debug_info("backend-fs2", "Error creating the src\n");
1743 free_session(PurpleMediaBackendFs2Session
*session
)
1745 g_free(session
->id
);
1750 create_participant(PurpleMediaBackendFs2
*self
, const gchar
*name
)
1752 PurpleMediaBackendFs2Private
*priv
=
1753 purple_media_backend_fs2_get_instance_private(self
);
1754 FsParticipant
*participant
;
1757 participant
= fs_conference_new_participant(
1758 priv
->conference
, &err
);
1761 purple_debug_error("backend-fs2",
1762 "Error creating participant: %s\n",
1768 g_object_set_data_full(G_OBJECT(participant
), "purple-name",
1769 g_strdup(name
), g_free
);
1771 if (g_object_class_find_property(G_OBJECT_GET_CLASS(participant
),
1773 g_object_set(participant
, "cname", name
, NULL
);
1776 if (!priv
->participants
) {
1777 purple_debug_info("backend-fs2",
1778 "Creating hash table for participants\n");
1779 priv
->participants
= g_hash_table_new_full(g_str_hash
,
1780 g_str_equal
, g_free
, g_object_unref
);
1783 g_hash_table_insert(priv
->participants
, g_strdup(name
), participant
);
1789 src_pad_added_cb_cb(PurpleMediaBackendFs2Stream
*stream
)
1791 PurpleMediaBackendFs2Private
*priv
;
1793 g_return_val_if_fail(stream
!= NULL
, FALSE
);
1795 priv
= purple_media_backend_fs2_get_instance_private(
1796 stream
->session
->backend
);
1797 stream
->connected_cb_id
= 0;
1799 purple_media_manager_create_output_window(
1800 purple_media_get_manager(priv
->media
), priv
->media
,
1801 stream
->session
->id
, stream
->participant
);
1803 g_signal_emit_by_name(priv
->media
, "state-changed",
1804 PURPLE_MEDIA_STATE_CONNECTED
,
1805 stream
->session
->id
, stream
->participant
);
1810 src_pad_added_cb(FsStream
*fsstream
, GstPad
*srcpad
,
1811 FsCodec
*codec
, PurpleMediaBackendFs2Stream
*stream
)
1813 PurpleMediaBackendFs2Private
*priv
;
1816 g_return_if_fail(FS_IS_STREAM(fsstream
));
1817 g_return_if_fail(stream
!= NULL
);
1819 priv
= purple_media_backend_fs2_get_instance_private(
1820 stream
->session
->backend
);
1822 if (stream
->src
== NULL
) {
1823 GstElement
*sink
= NULL
;
1825 if (codec
->media_type
== FS_MEDIA_TYPE_AUDIO
) {
1826 double output_volume
= purple_prefs_get_int(
1827 "/purple/media/audio/volume/output")/10.0;
1829 * Should this instead be:
1830 * audioconvert ! audioresample ! liveadder !
1831 * audioresample ! audioconvert ! realsink
1833 stream
->queue
= gst_element_factory_make("queue", NULL
);
1834 stream
->volume
= gst_element_factory_make("volume", NULL
);
1835 g_object_set(stream
->volume
, "volume", output_volume
, NULL
);
1836 stream
->level
= gst_element_factory_make("level", NULL
);
1837 stream
->src
= gst_element_factory_make("liveadder", NULL
);
1838 sink
= purple_media_manager_get_element(
1839 purple_media_get_manager(priv
->media
),
1840 PURPLE_MEDIA_RECV_AUDIO
, priv
->media
,
1841 stream
->session
->id
,
1842 stream
->participant
);
1843 gst_bin_add(GST_BIN(priv
->confbin
), stream
->queue
);
1844 gst_bin_add(GST_BIN(priv
->confbin
), stream
->volume
);
1845 gst_bin_add(GST_BIN(priv
->confbin
), stream
->level
);
1846 gst_bin_add(GST_BIN(priv
->confbin
), sink
);
1847 gst_element_set_state(sink
, GST_STATE_PLAYING
);
1848 gst_element_set_state(stream
->level
, GST_STATE_PLAYING
);
1849 gst_element_set_state(stream
->volume
, GST_STATE_PLAYING
);
1850 gst_element_set_state(stream
->queue
, GST_STATE_PLAYING
);
1851 gst_element_link(stream
->level
, sink
);
1852 gst_element_link(stream
->volume
, stream
->level
);
1853 gst_element_link(stream
->queue
, stream
->volume
);
1854 sink
= stream
->queue
;
1855 } else if (codec
->media_type
== FS_MEDIA_TYPE_VIDEO
) {
1856 stream
->src
= gst_element_factory_make("funnel", NULL
);
1857 sink
= gst_element_factory_make("fakesink", NULL
);
1858 g_object_set(G_OBJECT(sink
), "async", FALSE
, NULL
);
1859 gst_bin_add(GST_BIN(priv
->confbin
), sink
);
1860 gst_element_set_state(sink
, GST_STATE_PLAYING
);
1861 stream
->fakesink
= sink
;
1862 #ifdef HAVE_MEDIA_APPLICATION
1863 } else if (codec
->media_type
== FS_MEDIA_TYPE_APPLICATION
) {
1864 stream
->src
= gst_element_factory_make("funnel", NULL
);
1865 sink
= purple_media_manager_get_element(
1866 purple_media_get_manager(priv
->media
),
1867 PURPLE_MEDIA_RECV_APPLICATION
, priv
->media
,
1868 stream
->session
->id
,
1869 stream
->participant
);
1870 gst_bin_add(GST_BIN(priv
->confbin
), sink
);
1871 gst_element_set_state(sink
, GST_STATE_PLAYING
);
1874 stream
->tee
= gst_element_factory_make("tee", NULL
);
1875 gst_bin_add_many(GST_BIN(priv
->confbin
),
1876 stream
->src
, stream
->tee
, NULL
);
1877 gst_element_set_state(stream
->tee
, GST_STATE_PLAYING
);
1878 gst_element_set_state(stream
->src
, GST_STATE_PLAYING
);
1879 gst_element_link_many(stream
->src
, stream
->tee
, sink
, NULL
);
1882 sinkpad
= gst_element_get_request_pad(stream
->src
, "sink_%u");
1883 gst_pad_link(srcpad
, sinkpad
);
1884 gst_object_unref(sinkpad
);
1886 stream
->connected_cb_id
= g_timeout_add(0,
1887 (GSourceFunc
)src_pad_added_cb_cb
, stream
);
1891 create_stream(PurpleMediaBackendFs2
*self
,
1892 const gchar
*sess_id
, const gchar
*who
,
1893 PurpleMediaSessionType type
, gboolean initiator
,
1894 const gchar
*transmitter
,
1895 guint num_params
, GParameter
*params
)
1897 PurpleMediaBackendFs2Private
*priv
=
1898 purple_media_backend_fs2_get_instance_private(self
);
1900 FsStream
*fsstream
= NULL
;
1901 const gchar
*stun_ip
= purple_network_get_stun_ip();
1902 const gchar
*turn_ip
= purple_network_get_turn_ip();
1903 guint _num_params
= num_params
;
1904 GParameter
*_params
;
1905 FsStreamDirection type_direction
=
1906 session_type_to_fs_stream_direction(type
);
1907 PurpleMediaBackendFs2Session
*session
;
1908 PurpleMediaBackendFs2Stream
*stream
;
1909 FsParticipant
*participant
;
1910 /* check if the protocol has already specified a relay-info
1911 we need to do this to allow them to override when using non-standard
1912 TURN modes, like Google f.ex. */
1913 gboolean got_turn_from_protocol
= FALSE
;
1915 GPtrArray
*relay_info
= g_ptr_array_new_full (1, (GDestroyNotify
) gst_structure_free
);
1918 session
= get_session(self
, sess_id
);
1920 if (session
== NULL
) {
1921 purple_debug_error("backend-fs2",
1922 "Couldn't find session to create stream.\n");
1926 participant
= get_participant(self
, who
);
1928 if (participant
== NULL
) {
1929 purple_debug_error("backend-fs2", "Couldn't find "
1930 "participant to create stream.\n");
1934 fsstream
= fs_session_new_stream(session
->session
, participant
,
1935 initiator
== TRUE
? type_direction
:
1936 (type_direction
& FS_DIRECTION_RECV
), &err
);
1938 if (fsstream
== NULL
) {
1940 purple_debug_error("backend-fs2", "Error creating stream: %s\n",
1941 err
->message
? err
->message
: "NULL");
1944 purple_debug_error("backend-fs2",
1945 "Error creating stream\n");
1949 for (i
= 0 ; i
< num_params
; i
++) {
1950 if (purple_strequal(params
[i
].name
, "relay-info")) {
1951 got_turn_from_protocol
= TRUE
;
1956 _params
= g_new0(GParameter
, num_params
+ 3);
1957 memcpy(_params
, params
, sizeof(GParameter
) * num_params
);
1959 /* set the controlling mode parameter */
1960 _params
[_num_params
].name
= "controlling-mode";
1961 g_value_init(&_params
[_num_params
].value
, G_TYPE_BOOLEAN
);
1962 g_value_set_boolean(&_params
[_num_params
].value
, initiator
);
1966 purple_debug_info("backend-fs2",
1967 "Setting stun-ip on new stream: %s\n", stun_ip
);
1969 _params
[_num_params
].name
= "stun-ip";
1970 g_value_init(&_params
[_num_params
].value
, G_TYPE_STRING
);
1971 g_value_set_string(&_params
[_num_params
].value
, stun_ip
);
1975 if (turn_ip
&& purple_strequal("nice", transmitter
) && !got_turn_from_protocol
) {
1977 const gchar
*username
= purple_prefs_get_string(
1978 "/purple/network/turn_username");
1979 const gchar
*password
= purple_prefs_get_string(
1980 "/purple/network/turn_password");
1983 port
= purple_prefs_get_int("/purple/network/turn_port");
1985 g_ptr_array_add (relay_info
,
1986 gst_structure_new ("relay-info",
1987 "ip", G_TYPE_STRING
, turn_ip
,
1988 "port", G_TYPE_UINT
, port
,
1989 "username", G_TYPE_STRING
, username
,
1990 "password", G_TYPE_STRING
, password
,
1991 "relay-type", G_TYPE_STRING
, "udp",
1996 port
= purple_prefs_get_int("/purple/network/turn_port_tcp");
1998 g_ptr_array_add (relay_info
,
1999 gst_structure_new ("relay-info",
2000 "ip", G_TYPE_STRING
, turn_ip
,
2001 "port", G_TYPE_UINT
, port
,
2002 "username", G_TYPE_STRING
, username
,
2003 "password", G_TYPE_STRING
, password
,
2004 "relay-type", G_TYPE_STRING
, "tcp",
2008 /* TURN over SSL is only supported by libnice for Google's "psuedo" SSL mode
2011 purple_debug_info("backend-fs2",
2012 "Setting relay-info on new stream\n");
2013 _params
[_num_params
].name
= "relay-info";
2014 g_value_init(&_params
[_num_params
].value
, G_TYPE_PTR_ARRAY
);
2015 g_value_set_boxed(&_params
[_num_params
].value
, relay_info
);
2019 ret
= fs_stream_set_transmitter(fsstream
, transmitter
,
2020 _params
, _num_params
, &err
);
2021 for (i
= 0 ; i
< _num_params
; i
++)
2022 g_value_unset (&_params
[i
].value
);
2025 g_ptr_array_unref (relay_info
);
2027 purple_debug_error("backend-fs2",
2028 "Could not set transmitter %s: %s.\n",
2029 transmitter
, err
? err
->message
: NULL
);
2030 g_clear_error(&err
);
2034 stream
= g_new0(PurpleMediaBackendFs2Stream
, 1);
2035 stream
->participant
= g_strdup(who
);
2036 stream
->session
= session
;
2037 stream
->stream
= fsstream
;
2038 stream
->supports_add
= purple_strequal(transmitter
, "nice");
2040 priv
->streams
= g_list_append(priv
->streams
, stream
);
2042 g_signal_connect(G_OBJECT(fsstream
), "src-pad-added",
2043 G_CALLBACK(src_pad_added_cb
), stream
);
2049 free_stream(PurpleMediaBackendFs2Stream
*stream
)
2051 /* Remove the connected_cb timeout */
2052 if (stream
->connected_cb_id
!= 0)
2053 g_source_remove(stream
->connected_cb_id
);
2055 g_free(stream
->participant
);
2057 if (stream
->local_candidates
)
2058 fs_candidate_list_destroy(stream
->local_candidates
);
2060 if (stream
->remote_candidates
)
2061 fs_candidate_list_destroy(stream
->remote_candidates
);
2067 purple_media_backend_fs2_add_stream(PurpleMediaBackend
*self
,
2068 const gchar
*sess_id
, const gchar
*who
,
2069 PurpleMediaSessionType type
, gboolean initiator
,
2070 const gchar
*transmitter
,
2071 guint num_params
, GParameter
*params
)
2073 PurpleMediaBackendFs2
*backend
= PURPLE_MEDIA_BACKEND_FS2(self
);
2074 PurpleMediaBackendFs2Private
*priv
=
2075 purple_media_backend_fs2_get_instance_private(backend
);
2076 PurpleMediaBackendFs2Stream
*stream
;
2078 if (priv
->conference
== NULL
&& !init_conference(backend
)) {
2079 purple_debug_error("backend-fs2",
2080 "Error initializing the conference.\n");
2084 if (get_session(backend
, sess_id
) == NULL
&&
2085 !create_session(backend
, sess_id
, type
,
2086 initiator
, transmitter
)) {
2087 purple_debug_error("backend-fs2",
2088 "Error creating the session.\n");
2092 if (get_participant(backend
, who
) == NULL
&&
2093 !create_participant(backend
, who
)) {
2094 purple_debug_error("backend-fs2",
2095 "Error creating the participant.\n");
2099 stream
= get_stream(backend
, sess_id
, who
);
2101 if (stream
!= NULL
) {
2102 FsStreamDirection type_direction
=
2103 session_type_to_fs_stream_direction(type
);
2105 if (session_type_to_fs_stream_direction(
2106 stream
->session
->type
) != type_direction
) {
2107 /* change direction */
2108 g_object_set(stream
->stream
, "direction",
2109 type_direction
, NULL
);
2111 } else if (!create_stream(backend
, sess_id
, who
, type
,
2112 initiator
, transmitter
, num_params
, params
)) {
2113 purple_debug_error("backend-fs2",
2114 "Error creating the stream.\n");
2122 purple_media_backend_fs2_add_remote_candidates(PurpleMediaBackend
*self
,
2123 const gchar
*sess_id
, const gchar
*participant
,
2124 GList
*remote_candidates
)
2126 PurpleMediaBackendFs2Private
*priv
;
2127 PurpleMediaBackendFs2Stream
*stream
;
2130 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2132 priv
= purple_media_backend_fs2_get_instance_private(
2133 PURPLE_MEDIA_BACKEND_FS2(self
));
2134 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2135 sess_id
, participant
);
2137 if (stream
== NULL
) {
2138 purple_debug_error("backend-fs2",
2139 "purple_media_add_remote_candidates: "
2140 "couldn't find stream %s %s.\n",
2141 sess_id
? sess_id
: "(null)",
2142 participant
? participant
: "(null)");
2146 stream
->remote_candidates
= g_list_concat(stream
->remote_candidates
,
2147 candidate_list_to_fs(remote_candidates
));
2149 if (purple_media_is_initiator(priv
->media
, sess_id
, participant
) ||
2150 purple_media_accepted(
2151 priv
->media
, sess_id
, participant
)) {
2153 if (stream
->supports_add
)
2154 fs_stream_add_remote_candidates(stream
->stream
,
2155 stream
->remote_candidates
, &err
);
2157 fs_stream_force_remote_candidates(stream
->stream
,
2158 stream
->remote_candidates
, &err
);
2161 purple_debug_error("backend-fs2", "Error adding remote"
2162 " candidates: %s\n", err
->message
);
2169 purple_media_backend_fs2_codecs_ready(PurpleMediaBackend
*self
,
2170 const gchar
*sess_id
)
2172 PurpleMediaBackendFs2Private
*priv
;
2173 gboolean ret
= FALSE
;
2175 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2177 priv
= purple_media_backend_fs2_get_instance_private(
2178 PURPLE_MEDIA_BACKEND_FS2(self
));
2180 if (sess_id
!= NULL
) {
2181 PurpleMediaBackendFs2Session
*session
= get_session(
2182 PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2184 if (session
== NULL
)
2187 if (session
->type
& (PURPLE_MEDIA_SEND_AUDIO
|
2188 #ifdef HAVE_MEDIA_APPLICATION
2189 PURPLE_MEDIA_SEND_APPLICATION
|
2191 PURPLE_MEDIA_SEND_VIDEO
)) {
2193 GList
*codecs
= NULL
;
2195 g_object_get(session
->session
,
2196 "codecs", &codecs
, NULL
);
2198 fs_codec_list_destroy (codecs
);
2204 GList
*values
= g_hash_table_get_values(priv
->sessions
);
2206 for (; values
; values
= g_list_delete_link(values
, values
)) {
2207 PurpleMediaBackendFs2Session
*session
= values
->data
;
2209 if (session
->type
& (PURPLE_MEDIA_SEND_AUDIO
|
2210 #ifdef HAVE_MEDIA_APPLICATION
2211 PURPLE_MEDIA_SEND_APPLICATION
|
2213 PURPLE_MEDIA_SEND_VIDEO
)) {
2215 GList
*codecs
= NULL
;
2217 g_object_get(session
->session
,
2218 "codecs", &codecs
, NULL
);
2220 fs_codec_list_destroy (codecs
);
2231 g_list_free(values
);
2238 purple_media_backend_fs2_get_codecs(PurpleMediaBackend
*self
,
2239 const gchar
*sess_id
)
2241 PurpleMediaBackendFs2Session
*session
;
2245 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
2247 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2249 if (session
== NULL
)
2252 g_object_get(G_OBJECT(session
->session
),
2253 "codecs", &fscodecs
, NULL
);
2254 codecs
= codec_list_from_fs(fscodecs
);
2255 fs_codec_list_destroy(fscodecs
);
2261 purple_media_backend_fs2_get_local_candidates(PurpleMediaBackend
*self
,
2262 const gchar
*sess_id
, const gchar
*participant
)
2264 PurpleMediaBackendFs2Stream
*stream
;
2265 GList
*candidates
= NULL
;
2267 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
2269 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2270 sess_id
, participant
);
2273 candidates
= candidate_list_from_fs(
2274 stream
->local_candidates
);
2279 purple_media_backend_fs2_set_remote_codecs(PurpleMediaBackend
*self
,
2280 const gchar
*sess_id
, const gchar
*participant
,
2283 PurpleMediaBackendFs2Stream
*stream
;
2287 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2288 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2289 sess_id
, participant
);
2294 fscodecs
= codec_list_to_fs(codecs
);
2295 fs_stream_set_remote_codecs(stream
->stream
, fscodecs
, &err
);
2296 fs_codec_list_destroy(fscodecs
);
2299 purple_debug_error("backend-fs2",
2300 "Error setting remote codecs: %s\n",
2309 static GstStructure
*
2310 create_fs2_srtp_structure(const gchar
*cipher
, const gchar
*auth
,
2311 const gchar
*key
, gsize key_len
)
2313 GstStructure
*result
;
2317 buffer
= gst_buffer_new_allocate(NULL
, key_len
, NULL
);
2318 gst_buffer_map(buffer
, &info
, GST_MAP_WRITE
);
2319 memcpy(info
.data
, key
, key_len
);
2320 gst_buffer_unmap(buffer
, &info
);
2322 result
= gst_structure_new("FarstreamSRTP",
2323 "cipher", G_TYPE_STRING
, cipher
,
2324 "auth", G_TYPE_STRING
, auth
,
2325 "key", GST_TYPE_BUFFER
, buffer
,
2327 gst_buffer_unref(buffer
);
2333 purple_media_backend_fs2_set_encryption_parameters (PurpleMediaBackend
*self
,
2334 const gchar
*sess_id
, const gchar
*cipher
, const gchar
*auth
,
2335 const gchar
*key
, gsize key_len
)
2337 PurpleMediaBackendFs2Session
*session
;
2342 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2344 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2348 srtp
= create_fs2_srtp_structure(cipher
, auth
, key
, key_len
);
2352 result
= fs_session_set_encryption_parameters(session
->session
, srtp
,
2355 purple_debug_error("backend-fs2",
2356 "Error setting encryption parameters: %s\n", err
->message
);
2360 gst_structure_free(srtp
);
2365 purple_media_backend_fs2_set_decryption_parameters (PurpleMediaBackend
*self
,
2366 const gchar
*sess_id
, const gchar
*participant
,
2367 const gchar
*cipher
, const gchar
*auth
,
2368 const gchar
*key
, gsize key_len
)
2370 PurpleMediaBackendFs2Stream
*stream
;
2375 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2377 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
,
2382 srtp
= create_fs2_srtp_structure(cipher
, auth
, key
, key_len
);
2386 result
= fs_stream_set_decryption_parameters(stream
->stream
, srtp
,
2389 purple_debug_error("backend-fs2",
2390 "Error setting decryption parameters: %s\n", err
->message
);
2394 gst_structure_free(srtp
);
2399 purple_media_backend_fs2_set_send_codec(PurpleMediaBackend
*self
,
2400 const gchar
*sess_id
, PurpleMediaCodec
*codec
)
2402 PurpleMediaBackendFs2Session
*session
;
2406 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2408 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2410 if (session
== NULL
)
2413 fscodec
= codec_to_fs(codec
);
2414 fs_session_set_send_codec(session
->session
, fscodec
, &err
);
2415 fs_codec_destroy(fscodec
);
2418 purple_debug_error("media", "Error setting send codec\n");
2426 static const gchar
**
2427 purple_media_backend_fs2_get_available_params(void)
2429 static const gchar
*supported_params
[] = {
2430 "sdes-cname", "sdes-email", "sdes-location", "sdes-name", "sdes-note",
2431 "sdes-phone", "sdes-tool", NULL
2434 return supported_params
;
2438 param_to_sdes_type(const gchar
*param
)
2440 const gchar
**supported
= purple_media_backend_fs2_get_available_params();
2441 static const gchar
*sdes_types
[] = {
2442 "cname", "email", "location", "name", "note", "phone", "tool", NULL
2446 for (i
= 0; supported
[i
] != NULL
; ++i
) {
2447 if (purple_strequal(param
, supported
[i
])) {
2448 return sdes_types
[i
];
2456 purple_media_backend_fs2_set_params(PurpleMediaBackend
*self
,
2457 guint num_params
, GParameter
*params
)
2459 PurpleMediaBackendFs2Private
*priv
;
2463 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2465 priv
= purple_media_backend_fs2_get_instance_private(
2466 PURPLE_MEDIA_BACKEND_FS2(self
));
2468 if (priv
->conference
== NULL
&&
2469 !init_conference(PURPLE_MEDIA_BACKEND_FS2(self
))) {
2470 purple_debug_error("backend-fs2",
2471 "Error initializing the conference.\n");
2475 g_object_get(G_OBJECT(priv
->conference
), "sdes", &sdes
, NULL
);
2477 for (i
= 0; i
!= num_params
; ++i
) {
2478 const gchar
*sdes_type
= param_to_sdes_type(params
[i
].name
);
2482 gst_structure_set(sdes
, sdes_type
,
2483 G_TYPE_STRING
, g_value_get_string(¶ms
[i
].value
),
2487 g_object_set(G_OBJECT(priv
->conference
), "sdes", sdes
, NULL
);
2488 gst_structure_free(sdes
);
2491 send_dtmf_callback(gpointer userdata
)
2493 FsSession
*session
= userdata
;
2495 fs_session_stop_telephony_event(session
);
2500 purple_media_backend_fs2_send_dtmf(PurpleMediaBackend
*self
,
2501 const gchar
*sess_id
, gchar dtmf
, guint8 volume
,
2504 PurpleMediaBackendFs2Session
*session
;
2507 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2509 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2510 if (session
== NULL
)
2513 /* Convert DTMF char into FsDTMFEvent enum */
2515 case '0': event
= FS_DTMF_EVENT_0
; break;
2516 case '1': event
= FS_DTMF_EVENT_1
; break;
2517 case '2': event
= FS_DTMF_EVENT_2
; break;
2518 case '3': event
= FS_DTMF_EVENT_3
; break;
2519 case '4': event
= FS_DTMF_EVENT_4
; break;
2520 case '5': event
= FS_DTMF_EVENT_5
; break;
2521 case '6': event
= FS_DTMF_EVENT_6
; break;
2522 case '7': event
= FS_DTMF_EVENT_7
; break;
2523 case '8': event
= FS_DTMF_EVENT_8
; break;
2524 case '9': event
= FS_DTMF_EVENT_9
; break;
2525 case '*': event
= FS_DTMF_EVENT_STAR
; break;
2526 case '#': event
= FS_DTMF_EVENT_POUND
; break;
2527 case 'A': event
= FS_DTMF_EVENT_A
; break;
2528 case 'B': event
= FS_DTMF_EVENT_B
; break;
2529 case 'C': event
= FS_DTMF_EVENT_C
; break;
2530 case 'D': event
= FS_DTMF_EVENT_D
; break;
2535 if (!fs_session_start_telephony_event(session
->session
,
2540 if (duration
<= 50) {
2541 fs_session_stop_telephony_event(session
->session
);
2543 g_timeout_add(duration
, send_dtmf_callback
,
2551 purple_media_backend_fs2_get_type(void)
2558 purple_media_backend_fs2_get_src(PurpleMediaBackendFs2
*self
,
2559 const gchar
*sess_id
)
2562 PurpleMediaBackendFs2Session
*session
= get_session(self
, sess_id
);
2563 return session
!= NULL
? session
->src
: NULL
;
2570 purple_media_backend_fs2_get_tee(PurpleMediaBackendFs2
*self
,
2571 const gchar
*sess_id
, const gchar
*who
)
2574 if (sess_id
!= NULL
&& who
== NULL
) {
2575 PurpleMediaBackendFs2Session
*session
=
2576 get_session(self
, sess_id
);
2577 return (session
!= NULL
) ? session
->tee
: NULL
;
2578 } else if (sess_id
!= NULL
&& who
!= NULL
) {
2579 PurpleMediaBackendFs2Stream
*stream
=
2580 get_stream(self
, sess_id
, who
);
2581 return (stream
!= NULL
) ? stream
->tee
: NULL
;
2585 g_return_val_if_reached(NULL
);
2589 purple_media_backend_fs2_set_input_volume(PurpleMediaBackendFs2
*self
,
2590 const gchar
*sess_id
, double level
)
2593 PurpleMediaBackendFs2Private
*priv
;
2596 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2598 priv
= purple_media_backend_fs2_get_instance_private(self
);
2600 purple_prefs_set_int("/purple/media/audio/volume/input", level
);
2602 if (sess_id
== NULL
)
2603 sessions
= g_hash_table_get_values(priv
->sessions
);
2605 sessions
= g_list_append(NULL
, get_session(self
, sess_id
));
2607 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
2608 PurpleMediaBackendFs2Session
*session
= sessions
->data
;
2610 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
2611 gchar
*name
= g_strdup_printf("volume_%s",
2613 GstElement
*volume
= gst_bin_get_by_name(
2614 GST_BIN(priv
->confbin
), name
);
2616 g_object_set(volume
, "volume", level
/10.0, NULL
);
2623 purple_media_backend_fs2_set_output_volume(PurpleMediaBackendFs2
*self
,
2624 const gchar
*sess_id
, const gchar
*who
, double level
)
2629 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2631 purple_prefs_set_int("/purple/media/audio/volume/output", level
);
2633 streams
= get_streams(self
, sess_id
, who
);
2635 for (; streams
; streams
= g_list_delete_link(streams
, streams
)) {
2636 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
2638 if (stream
->session
->type
& PURPLE_MEDIA_RECV_AUDIO
2639 && GST_IS_ELEMENT(stream
->volume
)) {
2640 g_object_set(stream
->volume
, "volume",
2649 purple_media_backend_fs2_set_send_rtcp_mux(PurpleMediaBackend
*self
,
2650 const gchar
*sess_id
, const gchar
*participant
,
2651 gboolean send_rtcp_mux
)
2653 PurpleMediaBackendFs2Stream
*stream
;
2655 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2656 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2657 sess_id
, participant
);
2659 if (stream
!= NULL
&&
2660 g_object_class_find_property (G_OBJECT_GET_CLASS (stream
->stream
),
2661 "send-rtcp-mux") != NULL
) {
2662 g_object_set (stream
->stream
, "send-rtcp-mux", send_rtcp_mux
, NULL
);