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
);
397 g_list_free_full(priv
->streams
, (GDestroyNotify
)free_stream
);
399 if (priv
->sessions
) {
400 GList
*sessions
= g_hash_table_get_values(priv
->sessions
);
402 g_list_free_full(sessions
, (GDestroyNotify
)free_session
);
403 g_hash_table_destroy(priv
->sessions
);
406 G_OBJECT_CLASS(purple_media_backend_fs2_parent_class
)->finalize(obj
);
410 purple_media_backend_fs2_set_property(GObject
*object
, guint prop_id
,
411 const GValue
*value
, GParamSpec
*pspec
)
413 PurpleMediaBackendFs2Private
*priv
;
414 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object
));
416 priv
= purple_media_backend_fs2_get_instance_private(
417 PURPLE_MEDIA_BACKEND_FS2(object
));
420 case PROP_CONFERENCE_TYPE
:
421 priv
->conference_type
= g_value_dup_string(value
);
424 priv
->media
= g_value_get_object(value
);
426 if (priv
->media
== NULL
)
429 g_object_add_weak_pointer(G_OBJECT(priv
->media
),
430 (gpointer
*)&priv
->media
);
432 g_signal_connect(G_OBJECT(priv
->media
),
434 G_CALLBACK(state_changed_cb
),
435 PURPLE_MEDIA_BACKEND_FS2(object
));
436 g_signal_connect(G_OBJECT(priv
->media
), "stream-info",
437 G_CALLBACK(stream_info_cb
),
438 PURPLE_MEDIA_BACKEND_FS2(object
));
441 G_OBJECT_WARN_INVALID_PROPERTY_ID(
442 object
, prop_id
, pspec
);
448 purple_media_backend_fs2_get_property(GObject
*object
, guint prop_id
,
449 GValue
*value
, GParamSpec
*pspec
)
451 PurpleMediaBackendFs2Private
*priv
;
452 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object
));
454 priv
= purple_media_backend_fs2_get_instance_private(
455 PURPLE_MEDIA_BACKEND_FS2(object
));
458 case PROP_CONFERENCE_TYPE
:
459 g_value_set_string(value
, priv
->conference_type
);
462 g_value_set_object(value
, priv
->media
);
465 G_OBJECT_WARN_INVALID_PROPERTY_ID(
466 object
, prop_id
, pspec
);
472 purple_media_backend_fs2_class_init(PurpleMediaBackendFs2Class
*klass
)
474 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
478 gobject_class
->dispose
= purple_media_backend_fs2_dispose
;
479 gobject_class
->finalize
= purple_media_backend_fs2_finalize
;
480 gobject_class
->set_property
= purple_media_backend_fs2_set_property
;
481 gobject_class
->get_property
= purple_media_backend_fs2_get_property
;
483 g_object_class_override_property(gobject_class
, PROP_CONFERENCE_TYPE
,
485 g_object_class_override_property(gobject_class
, PROP_MEDIA
, "media");
487 /* VA-API elements aren't well supported in Farstream. Ignore them. */
488 features
= gst_registry_get_feature_list_by_plugin(gst_registry_get(),
490 g_list_foreach(features
, (GFunc
)gst_plugin_feature_set_rank
, GINT_TO_POINTER(GST_RANK_NONE
));
491 gst_plugin_feature_list_free(features
);
495 purple_media_backend_iface_init(PurpleMediaBackendInterface
*iface
)
497 iface
->add_stream
= purple_media_backend_fs2_add_stream
;
498 iface
->add_remote_candidates
=
499 purple_media_backend_fs2_add_remote_candidates
;
500 iface
->codecs_ready
= purple_media_backend_fs2_codecs_ready
;
501 iface
->get_codecs
= purple_media_backend_fs2_get_codecs
;
502 iface
->get_local_candidates
=
503 purple_media_backend_fs2_get_local_candidates
;
504 iface
->set_remote_codecs
= purple_media_backend_fs2_set_remote_codecs
;
505 iface
->set_send_codec
= purple_media_backend_fs2_set_send_codec
;
506 iface
->set_encryption_parameters
=
507 purple_media_backend_fs2_set_encryption_parameters
;
508 iface
->set_decryption_parameters
=
509 purple_media_backend_fs2_set_decryption_parameters
;
510 iface
->set_params
= purple_media_backend_fs2_set_params
;
511 iface
->get_available_params
= purple_media_backend_fs2_get_available_params
;
512 iface
->send_dtmf
= purple_media_backend_fs2_send_dtmf
;
513 iface
->set_send_rtcp_mux
= purple_media_backend_fs2_set_send_rtcp_mux
;
517 session_type_to_fs_media_type(PurpleMediaSessionType type
)
519 if (type
& PURPLE_MEDIA_AUDIO
)
520 return FS_MEDIA_TYPE_AUDIO
;
521 else if (type
& PURPLE_MEDIA_VIDEO
)
522 return FS_MEDIA_TYPE_VIDEO
;
523 #ifdef HAVE_MEDIA_APPLICATION
524 else if (type
& PURPLE_MEDIA_APPLICATION
)
525 return FS_MEDIA_TYPE_APPLICATION
;
531 static FsStreamDirection
532 session_type_to_fs_stream_direction(PurpleMediaSessionType type
)
534 if ((type
& PURPLE_MEDIA_AUDIO
) == PURPLE_MEDIA_AUDIO
||
535 (type
& PURPLE_MEDIA_VIDEO
) == PURPLE_MEDIA_VIDEO
)
536 return FS_DIRECTION_BOTH
;
537 else if ((type
& PURPLE_MEDIA_SEND_AUDIO
) ||
538 (type
& PURPLE_MEDIA_SEND_VIDEO
))
539 return FS_DIRECTION_SEND
;
540 else if ((type
& PURPLE_MEDIA_RECV_AUDIO
) ||
541 (type
& PURPLE_MEDIA_RECV_VIDEO
))
542 return FS_DIRECTION_RECV
;
543 #ifdef HAVE_MEDIA_APPLICATION
544 else if ((type
& PURPLE_MEDIA_APPLICATION
) == PURPLE_MEDIA_APPLICATION
)
545 return FS_DIRECTION_BOTH
;
546 else if (type
& PURPLE_MEDIA_SEND_APPLICATION
)
547 return FS_DIRECTION_SEND
;
548 else if (type
& PURPLE_MEDIA_RECV_APPLICATION
)
549 return FS_DIRECTION_RECV
;
552 return FS_DIRECTION_NONE
;
555 static PurpleMediaSessionType
556 session_type_from_fs(FsMediaType type
, FsStreamDirection direction
)
558 PurpleMediaSessionType result
= PURPLE_MEDIA_NONE
;
559 if (type
== FS_MEDIA_TYPE_AUDIO
) {
560 if (direction
& FS_DIRECTION_SEND
)
561 result
|= PURPLE_MEDIA_SEND_AUDIO
;
562 if (direction
& FS_DIRECTION_RECV
)
563 result
|= PURPLE_MEDIA_RECV_AUDIO
;
564 } else if (type
== FS_MEDIA_TYPE_VIDEO
) {
565 if (direction
& FS_DIRECTION_SEND
)
566 result
|= PURPLE_MEDIA_SEND_VIDEO
;
567 if (direction
& FS_DIRECTION_RECV
)
568 result
|= PURPLE_MEDIA_RECV_VIDEO
;
569 #ifdef HAVE_MEDIA_APPLICATION
570 } else if (type
== FS_MEDIA_TYPE_APPLICATION
) {
571 if (direction
& FS_DIRECTION_SEND
)
572 result
|= PURPLE_MEDIA_SEND_APPLICATION
;
573 if (direction
& FS_DIRECTION_RECV
)
574 result
|= PURPLE_MEDIA_RECV_APPLICATION
;
581 candidate_to_fs(PurpleMediaCandidate
*candidate
)
583 FsCandidate
*fscandidate
;
590 PurpleMediaNetworkProtocol proto
;
592 PurpleMediaCandidateType type
;
597 if (candidate
== NULL
)
600 g_object_get(G_OBJECT(candidate
),
601 "foundation", &foundation
,
602 "component-id", &component_id
,
606 "base-port", &base_port
,
608 "priority", &priority
,
610 "username", &username
,
611 "password", &password
,
615 fscandidate
= fs_candidate_new(foundation
,
616 component_id
, purple_media_candidate_type_to_fs(type
),
617 purple_media_network_protocol_to_fs(proto
), ip
, port
);
619 fscandidate
->base_ip
= base_ip
;
620 fscandidate
->base_port
= base_port
;
621 fscandidate
->priority
= priority
;
622 fscandidate
->username
= username
;
623 fscandidate
->password
= password
;
624 fscandidate
->ttl
= ttl
;
632 candidate_list_to_fs(GList
*candidates
)
634 GList
*new_list
= NULL
;
636 for (; candidates
; candidates
= g_list_next(candidates
)) {
637 new_list
= g_list_prepend(new_list
,
638 candidate_to_fs(candidates
->data
));
641 new_list
= g_list_reverse(new_list
);
645 static PurpleMediaCandidate
*
646 candidate_from_fs(FsCandidate
*fscandidate
)
648 PurpleMediaCandidate
*candidate
;
650 if (fscandidate
== NULL
)
653 candidate
= purple_media_candidate_new(fscandidate
->foundation
,
654 fscandidate
->component_id
,
655 purple_media_candidate_type_from_fs(fscandidate
->type
),
656 purple_media_network_protocol_from_fs(fscandidate
->proto
),
657 fscandidate
->ip
, fscandidate
->port
);
658 g_object_set(candidate
,
659 "base-ip", fscandidate
->base_ip
,
660 "base-port", fscandidate
->base_port
,
661 "priority", fscandidate
->priority
,
662 "username", fscandidate
->username
,
663 "password", fscandidate
->password
,
664 "ttl", fscandidate
->ttl
, NULL
);
669 candidate_list_from_fs(GList
*candidates
)
671 GList
*new_list
= NULL
;
673 for (; candidates
; candidates
= g_list_next(candidates
)) {
674 new_list
= g_list_prepend(new_list
,
675 candidate_from_fs(candidates
->data
));
678 new_list
= g_list_reverse(new_list
);
683 codec_to_fs(const PurpleMediaCodec
*codec
)
688 PurpleMediaSessionType media_type
;
696 g_object_get(G_OBJECT(codec
),
698 "encoding-name", &encoding_name
,
699 "media-type", &media_type
,
700 "clock-rate", &clock_rate
,
701 "channels", &channels
,
702 "optional-params", &iter
,
705 new_codec
= fs_codec_new(id
, encoding_name
,
706 session_type_to_fs_media_type(media_type
),
708 new_codec
->channels
= channels
;
710 for (; iter
; iter
= g_list_next(iter
)) {
711 PurpleKeyValuePair
*param
= (PurpleKeyValuePair
*)iter
->data
;
712 fs_codec_add_optional_parameter(new_codec
,
713 param
->key
, param
->value
);
716 g_free(encoding_name
);
720 static PurpleMediaCodec
*
721 codec_from_fs(const FsCodec
*codec
)
723 PurpleMediaCodec
*new_codec
;
729 new_codec
= purple_media_codec_new(codec
->id
, codec
->encoding_name
,
730 session_type_from_fs(codec
->media_type
,
731 FS_DIRECTION_BOTH
), codec
->clock_rate
);
732 g_object_set(new_codec
, "channels", codec
->channels
, NULL
);
734 for (iter
= codec
->optional_params
; iter
; iter
= g_list_next(iter
)) {
735 FsCodecParameter
*param
= (FsCodecParameter
*)iter
->data
;
736 purple_media_codec_add_optional_parameter(new_codec
,
737 param
->name
, param
->value
);
744 codec_list_from_fs(GList
*codecs
)
746 GList
*new_list
= NULL
;
748 for (; codecs
; codecs
= g_list_next(codecs
)) {
749 new_list
= g_list_prepend(new_list
,
750 codec_from_fs(codecs
->data
));
753 new_list
= g_list_reverse(new_list
);
758 codec_list_to_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_to_fs(codecs
->data
));
767 new_list
= g_list_reverse(new_list
);
771 static PurpleMediaBackendFs2Session
*
772 get_session(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
)
774 PurpleMediaBackendFs2Private
*priv
;
775 PurpleMediaBackendFs2Session
*session
= NULL
;
777 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
779 priv
= purple_media_backend_fs2_get_instance_private(self
);
781 if (priv
->sessions
!= NULL
)
782 session
= g_hash_table_lookup(priv
->sessions
, sess_id
);
787 static FsParticipant
*
788 get_participant(PurpleMediaBackendFs2
*self
, const gchar
*name
)
790 PurpleMediaBackendFs2Private
*priv
;
791 FsParticipant
*participant
= NULL
;
793 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
795 priv
= purple_media_backend_fs2_get_instance_private(self
);
797 if (priv
->participants
!= NULL
)
798 participant
= g_hash_table_lookup(priv
->participants
, name
);
803 static PurpleMediaBackendFs2Stream
*
804 get_stream(PurpleMediaBackendFs2
*self
,
805 const gchar
*sess_id
, const gchar
*name
)
807 PurpleMediaBackendFs2Private
*priv
;
810 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
812 priv
= purple_media_backend_fs2_get_instance_private(self
);
813 streams
= priv
->streams
;
815 for (; streams
; streams
= g_list_next(streams
)) {
816 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
817 if (purple_strequal(stream
->session
->id
, sess_id
) &&
818 purple_strequal(stream
->participant
, name
))
826 get_streams(PurpleMediaBackendFs2
*self
,
827 const gchar
*sess_id
, const gchar
*name
)
829 PurpleMediaBackendFs2Private
*priv
;
830 GList
*streams
, *ret
= NULL
;
832 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
834 priv
= purple_media_backend_fs2_get_instance_private(self
);
835 streams
= priv
->streams
;
837 for (; streams
; streams
= g_list_next(streams
)) {
838 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
840 if (sess_id
!= NULL
&& !purple_strequal(stream
->session
->id
, sess_id
))
842 else if (name
!= NULL
&& !purple_strequal(stream
->participant
, name
))
845 ret
= g_list_prepend(ret
, stream
);
848 ret
= g_list_reverse(ret
);
852 static PurpleMediaBackendFs2Session
*
853 get_session_from_fs_stream(PurpleMediaBackendFs2
*self
, FsStream
*stream
)
855 PurpleMediaBackendFs2Private
*priv
=
856 purple_media_backend_fs2_get_instance_private(self
);
857 FsSession
*fssession
;
860 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
861 g_return_val_if_fail(FS_IS_STREAM(stream
), NULL
);
863 g_object_get(stream
, "session", &fssession
, NULL
);
865 values
= g_hash_table_get_values(priv
->sessions
);
867 for (; values
; values
= g_list_delete_link(values
, values
)) {
868 PurpleMediaBackendFs2Session
*session
= values
->data
;
870 if (session
->session
== fssession
) {
872 g_object_unref(fssession
);
877 g_object_unref(fssession
);
882 gst_msg_db_to_percent(GstMessage
*msg
, gchar
*value_name
)
889 list
= gst_structure_get_value(gst_message_get_structure(msg
), value_name
);
890 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
891 value
= g_value_array_get_nth(g_value_get_boxed(list
), 0);
892 G_GNUC_END_IGNORE_DEPRECATIONS
893 value_db
= g_value_get_double(value
);
894 percent
= pow(10, value_db
/ 20);
895 return (percent
> 1.0) ? 1.0 : percent
;
899 purple_media_error_fs(PurpleMedia
*media
, const gchar
*error
,
900 const GstStructure
*fs_error
)
902 const gchar
*error_msg
= gst_structure_get_string(fs_error
, "error-msg");
904 purple_media_error(media
, "%s%s%s", error
,
905 error_msg
? _("\n\nMessage from Farstream: ") : "",
906 error_msg
? error_msg
: "");
910 gst_handle_message_element(GstBus
*bus
, GstMessage
*msg
,
911 PurpleMediaBackendFs2
*self
)
913 PurpleMediaBackendFs2Private
*priv
=
914 purple_media_backend_fs2_get_instance_private(self
);
915 GstElement
*src
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
916 static guint level_id
= 0;
917 const GstStructure
*structure
= gst_message_get_structure(msg
);
920 level_id
= g_signal_lookup("level", PURPLE_TYPE_MEDIA
);
922 if (gst_structure_has_name(structure
, "level")) {
923 GstElement
*src
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
925 gchar
*participant
= NULL
;
926 PurpleMediaBackendFs2Session
*session
= NULL
;
929 if (!PURPLE_IS_MEDIA(priv
->media
) ||
930 GST_ELEMENT_PARENT(src
) != priv
->confbin
)
933 name
= gst_element_get_name(src
);
935 if (!strncmp(name
, "sendlevel_", 10)) {
936 session
= get_session(self
, name
+10);
937 if (priv
->silence_threshold
> 0) {
938 percent
= gst_msg_db_to_percent(msg
, "decay");
939 g_object_set(session
->srcvalve
,
940 "drop", (percent
< priv
->silence_threshold
), NULL
);
946 if (!g_signal_has_handler_pending(priv
->media
, level_id
, 0, FALSE
))
950 GList
*iter
= priv
->streams
;
951 PurpleMediaBackendFs2Stream
*stream
;
952 for (; iter
; iter
= g_list_next(iter
)) {
954 if (stream
->level
== src
) {
955 session
= stream
->session
;
956 participant
= stream
->participant
;
965 percent
= gst_msg_db_to_percent(msg
, "rms");
967 g_signal_emit(priv
->media
, level_id
, 0,
968 session
->id
, participant
, percent
);
972 if (!FS_IS_CONFERENCE(src
) || !PURPLE_IS_MEDIA_BACKEND(self
) ||
973 priv
->conference
!= FS_CONFERENCE(src
))
976 if (gst_structure_has_name(structure
, "farstream-error")) {
978 gboolean error_emitted
= FALSE
;
979 gst_structure_get_enum(structure
, "error-no",
980 FS_TYPE_ERROR
, (gint
*)&error_no
);
982 case FS_ERROR_CONSTRUCTION
:
983 purple_media_error_fs(priv
->media
,
984 _("Error initializing the call. "
985 "This probably denotes problem in "
986 "installation of GStreamer or Farstream."),
988 error_emitted
= TRUE
;
990 case FS_ERROR_NETWORK
:
991 purple_media_error_fs(priv
->media
, _("Network error."),
993 error_emitted
= TRUE
;
994 purple_media_end(priv
->media
, NULL
, NULL
);
996 case FS_ERROR_NEGOTIATION_FAILED
:
997 purple_media_error_fs(priv
->media
,
998 _("Codec negotiation failed. "
999 "This problem might be resolved by installing "
1000 "more GStreamer codecs."),
1002 error_emitted
= TRUE
;
1003 purple_media_end(priv
->media
, NULL
, NULL
);
1005 case FS_ERROR_NO_CODECS
:
1006 purple_media_error(priv
->media
,
1007 _("No codecs found. "
1008 "Install some GStreamer codecs found "
1009 "in GStreamer plugins packages."));
1010 error_emitted
= TRUE
;
1011 purple_media_end(priv
->media
, NULL
, NULL
);
1014 purple_debug_error("backend-fs2",
1015 "farstream-error: %i: %s\n",
1017 gst_structure_get_string(structure
, "error-msg"));
1021 if (FS_ERROR_IS_FATAL(error_no
)) {
1023 purple_media_error(priv
->media
,
1024 _("A non-recoverable Farstream error has occurred."));
1025 purple_media_end(priv
->media
, NULL
, NULL
);
1027 } else if (gst_structure_has_name(structure
,
1028 "farstream-new-local-candidate")) {
1029 const GValue
*value
;
1031 FsCandidate
*local_candidate
;
1032 PurpleMediaCandidate
*candidate
;
1033 FsParticipant
*participant
;
1034 PurpleMediaBackendFs2Session
*session
;
1035 PurpleMediaBackendFs2Stream
*media_stream
;
1038 value
= gst_structure_get_value(structure
, "stream");
1039 stream
= g_value_get_object(value
);
1040 value
= gst_structure_get_value(structure
, "candidate");
1041 local_candidate
= g_value_get_boxed(value
);
1043 session
= get_session_from_fs_stream(self
, stream
);
1045 purple_debug_info("backend-fs2",
1046 "got new local candidate: %s\n",
1047 local_candidate
->foundation
);
1049 g_object_get(stream
, "participant", &participant
, NULL
);
1050 name
= g_object_get_data(G_OBJECT(participant
), "purple-name");
1052 media_stream
= get_stream(self
, session
->id
, name
);
1053 media_stream
->local_candidates
= g_list_append(
1054 media_stream
->local_candidates
,
1055 fs_candidate_copy(local_candidate
));
1057 candidate
= candidate_from_fs(local_candidate
);
1058 g_signal_emit_by_name(self
, "new-candidate",
1059 session
->id
, name
, candidate
);
1060 g_object_unref(candidate
);
1061 g_object_unref(participant
);
1062 } else if (gst_structure_has_name(structure
,
1063 "farstream-local-candidates-prepared")) {
1064 const GValue
*value
;
1066 FsParticipant
*participant
;
1067 PurpleMediaBackendFs2Session
*session
;
1069 value
= gst_structure_get_value(structure
, "stream");
1070 stream
= g_value_get_object(value
);
1071 session
= get_session_from_fs_stream(self
, stream
);
1073 g_object_get(stream
, "participant", &participant
, NULL
);
1075 g_signal_emit_by_name(self
, "candidates-prepared",
1077 g_object_get_data(G_OBJECT(participant
), "purple-name"));
1079 g_object_unref(participant
);
1080 } else if (gst_structure_has_name(structure
,
1081 "farstream-new-active-candidate-pair")) {
1082 const GValue
*value
;
1084 FsCandidate
*local_candidate
;
1085 FsCandidate
*remote_candidate
;
1086 FsParticipant
*participant
;
1087 PurpleMediaBackendFs2Session
*session
;
1088 PurpleMediaCandidate
*lcandidate
, *rcandidate
;
1090 value
= gst_structure_get_value(structure
, "stream");
1091 stream
= g_value_get_object(value
);
1092 value
= gst_structure_get_value(structure
, "local-candidate");
1093 local_candidate
= g_value_get_boxed(value
);
1094 value
= gst_structure_get_value(structure
, "remote-candidate");
1095 remote_candidate
= g_value_get_boxed(value
);
1097 g_object_get(stream
, "participant", &participant
, NULL
);
1099 session
= get_session_from_fs_stream(self
, stream
);
1101 lcandidate
= candidate_from_fs(local_candidate
);
1102 rcandidate
= candidate_from_fs(remote_candidate
);
1104 g_signal_emit_by_name(self
, "active-candidate-pair",
1106 g_object_get_data(G_OBJECT(participant
), "purple-name"),
1107 lcandidate
, rcandidate
);
1109 g_object_unref(participant
);
1110 g_object_unref(lcandidate
);
1111 g_object_unref(rcandidate
);
1112 } else if (gst_structure_has_name(structure
,
1113 "farstream-recv-codecs-changed")) {
1114 const GValue
*value
;
1118 value
= gst_structure_get_value(structure
, "codecs");
1119 codecs
= g_value_get_boxed(value
);
1120 codec
= codecs
->data
;
1122 purple_debug_info("backend-fs2",
1123 "farstream-recv-codecs-changed: %s\n",
1124 codec
->encoding_name
);
1125 } else if (gst_structure_has_name(structure
,
1126 "farstream-component-state-changed")) {
1127 const GValue
*value
;
1128 FsStreamState fsstate
;
1132 value
= gst_structure_get_value(structure
, "state");
1133 fsstate
= g_value_get_enum(value
);
1134 value
= gst_structure_get_value(structure
, "component");
1135 component
= g_value_get_uint(value
);
1138 case FS_STREAM_STATE_FAILED
:
1141 case FS_STREAM_STATE_DISCONNECTED
:
1142 state
= "DISCONNECTED";
1144 case FS_STREAM_STATE_GATHERING
:
1145 state
= "GATHERING";
1147 case FS_STREAM_STATE_CONNECTING
:
1148 state
= "CONNECTING";
1150 case FS_STREAM_STATE_CONNECTED
:
1151 state
= "CONNECTED";
1153 case FS_STREAM_STATE_READY
:
1161 purple_debug_info("backend-fs2",
1162 "farstream-component-state-changed: "
1163 "component: %u state: %s\n",
1165 } else if (gst_structure_has_name(structure
,
1166 "farstream-send-codec-changed")) {
1167 const GValue
*value
;
1171 value
= gst_structure_get_value(structure
, "codec");
1172 codec
= g_value_get_boxed(value
);
1173 codec_str
= fs_codec_to_string(codec
);
1175 purple_debug_info("backend-fs2",
1176 "farstream-send-codec-changed: codec: %s\n",
1180 } else if (gst_structure_has_name(structure
,
1181 "farstream-codecs-changed")) {
1182 const GValue
*value
;
1183 FsSession
*fssession
;
1186 value
= gst_structure_get_value(structure
, "session");
1187 fssession
= g_value_get_object(value
);
1188 sessions
= g_hash_table_get_values(priv
->sessions
);
1190 for (; sessions
; sessions
=
1191 g_list_delete_link(sessions
, sessions
)) {
1192 PurpleMediaBackendFs2Session
*session
= sessions
->data
;
1195 if (session
->session
!= fssession
)
1198 session_id
= g_strdup(session
->id
);
1199 g_signal_emit_by_name(self
, "codecs-changed",
1202 g_list_free(sessions
);
1209 gst_handle_message_error(GstBus
*bus
, GstMessage
*msg
,
1210 PurpleMediaBackendFs2
*self
)
1212 PurpleMediaBackendFs2Private
*priv
=
1213 purple_media_backend_fs2_get_instance_private(self
);
1214 GstElement
*element
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
1215 GstElement
*lastElement
= NULL
;
1218 GError
*error
= NULL
;
1219 gchar
*debug_msg
= NULL
;
1221 gst_message_parse_error(msg
, &error
, &debug_msg
);
1222 purple_debug_error("backend-fs2", "gst error %s\ndebugging: %s\n",
1223 error
->message
, debug_msg
);
1225 g_error_free(error
);
1228 while (element
&& !GST_IS_PIPELINE(element
)) {
1229 if (element
== priv
->confbin
)
1232 lastElement
= element
;
1233 element
= GST_ELEMENT_PARENT(element
);
1236 if (!element
|| !GST_IS_PIPELINE(element
))
1239 sessions
= purple_media_get_session_ids(priv
->media
);
1241 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
1242 if (purple_media_get_src(priv
->media
, sessions
->data
)
1246 if (purple_media_get_session_type(priv
->media
, sessions
->data
)
1247 & PURPLE_MEDIA_AUDIO
)
1248 purple_media_error(priv
->media
,
1249 _("Error with your microphone"));
1250 else if (purple_media_get_session_type(priv
->media
,
1251 sessions
->data
) & PURPLE_MEDIA_VIDEO
)
1252 purple_media_error(priv
->media
,
1253 _("Error with your webcam"));
1258 g_list_free(sessions
);
1260 purple_media_error(priv
->media
, _("Conference error"));
1261 purple_media_end(priv
->media
, NULL
, NULL
);
1265 gst_bus_cb(GstBus
*bus
, GstMessage
*msg
, PurpleMediaBackendFs2
*self
)
1267 switch(GST_MESSAGE_TYPE(msg
)) {
1268 case GST_MESSAGE_ELEMENT
:
1269 gst_handle_message_element(bus
, msg
, self
);
1271 case GST_MESSAGE_ERROR
:
1272 gst_handle_message_error(bus
, msg
, self
);
1282 remove_element(GstElement
*element
)
1285 gst_element_set_locked_state(element
, TRUE
);
1286 gst_element_set_state(element
, GST_STATE_NULL
);
1287 gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(element
)), element
);
1292 state_changed_cb(PurpleMedia
*media
, PurpleMediaState state
,
1293 gchar
*sid
, gchar
*name
, PurpleMediaBackendFs2
*self
)
1295 if (state
== PURPLE_MEDIA_STATE_END
) {
1296 PurpleMediaBackendFs2Private
*priv
=
1297 purple_media_backend_fs2_get_instance_private(
1301 PurpleMediaBackendFs2Stream
*stream
= get_stream(self
, sid
, name
);
1302 gst_object_unref(stream
->stream
);
1304 priv
->streams
= g_list_remove(priv
->streams
, stream
);
1306 remove_element(stream
->src
);
1307 remove_element(stream
->tee
);
1308 remove_element(stream
->volume
);
1309 remove_element(stream
->level
);
1310 remove_element(stream
->fakesink
);
1311 remove_element(stream
->queue
);
1313 free_stream(stream
);
1314 } else if (sid
&& !name
) {
1315 PurpleMediaBackendFs2Session
*session
= get_session(self
, sid
);
1318 g_object_get(session
->session
, "sink-pad", &pad
, NULL
);
1319 gst_pad_unlink(GST_PAD_PEER(pad
), pad
);
1320 gst_object_unref(pad
);
1322 gst_object_unref(session
->session
);
1323 g_hash_table_remove(priv
->sessions
, session
->id
);
1325 pad
= gst_pad_get_peer(session
->srcpad
);
1326 gst_element_remove_pad(GST_ELEMENT_PARENT(pad
), pad
);
1327 gst_object_unref(pad
);
1328 gst_object_unref(session
->srcpad
);
1330 remove_element(session
->srcvalve
);
1331 remove_element(session
->tee
);
1333 free_session(session
);
1336 purple_media_manager_remove_output_windows(
1337 purple_media_get_manager(media
), media
, sid
, name
);
1342 stream_info_cb(PurpleMedia
*media
, PurpleMediaInfoType type
,
1343 gchar
*sid
, gchar
*name
, gboolean local
,
1344 PurpleMediaBackendFs2
*self
)
1346 if (type
== PURPLE_MEDIA_INFO_ACCEPT
&& sid
!= NULL
&& name
!= NULL
) {
1347 PurpleMediaBackendFs2Stream
*stream
=
1348 get_stream(self
, sid
, name
);
1351 g_object_set(G_OBJECT(stream
->stream
), "direction",
1352 session_type_to_fs_stream_direction(
1353 stream
->session
->type
), NULL
);
1355 if (stream
->remote_candidates
== NULL
||
1356 purple_media_is_initiator(media
, sid
, name
))
1359 if (stream
->supports_add
)
1360 fs_stream_add_remote_candidates(stream
->stream
,
1361 stream
->remote_candidates
, &err
);
1363 fs_stream_force_remote_candidates(stream
->stream
,
1364 stream
->remote_candidates
, &err
);
1369 purple_debug_error("backend-fs2", "Error adding "
1370 "remote candidates: %s\n",
1373 } else if (local
== TRUE
&& (type
== PURPLE_MEDIA_INFO_MUTE
||
1374 type
== PURPLE_MEDIA_INFO_UNMUTE
)) {
1375 PurpleMediaBackendFs2Private
*priv
=
1376 purple_media_backend_fs2_get_instance_private(
1378 gboolean active
= (type
== PURPLE_MEDIA_INFO_MUTE
);
1382 sessions
= g_hash_table_get_values(priv
->sessions
);
1384 sessions
= g_list_prepend(NULL
,
1385 get_session(self
, sid
));
1387 purple_debug_info("media", "Turning mute %s\n",
1388 active
? "on" : "off");
1390 for (; sessions
; sessions
= g_list_delete_link(
1391 sessions
, sessions
)) {
1392 PurpleMediaBackendFs2Session
*session
=
1395 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
1396 gchar
*name
= g_strdup_printf("volume_%s",
1398 GstElement
*volume
= gst_bin_get_by_name(
1399 GST_BIN(priv
->confbin
), name
);
1401 g_object_set(volume
, "mute", active
, NULL
);
1404 } else if (local
== TRUE
&& (type
== PURPLE_MEDIA_INFO_HOLD
||
1405 type
== PURPLE_MEDIA_INFO_UNHOLD
)) {
1406 gboolean active
= (type
== PURPLE_MEDIA_INFO_HOLD
);
1407 GList
*streams
= get_streams(self
, sid
, name
);
1408 for (; streams
; streams
=
1409 g_list_delete_link(streams
, streams
)) {
1410 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
1411 if (stream
->session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
1412 g_object_set(stream
->stream
, "direction",
1413 session_type_to_fs_stream_direction(
1414 stream
->session
->type
& ((active
) ?
1415 ~PURPLE_MEDIA_SEND_AUDIO
:
1416 PURPLE_MEDIA_AUDIO
)), NULL
);
1419 } else if (local
== TRUE
&& (type
== PURPLE_MEDIA_INFO_PAUSE
||
1420 type
== PURPLE_MEDIA_INFO_UNPAUSE
)) {
1421 gboolean active
= (type
== PURPLE_MEDIA_INFO_PAUSE
);
1422 GList
*streams
= get_streams(self
, sid
, name
);
1423 for (; streams
; streams
=
1424 g_list_delete_link(streams
, streams
)) {
1425 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
1426 if (stream
->session
->type
& PURPLE_MEDIA_SEND_VIDEO
) {
1427 g_object_set(stream
->stream
, "direction",
1428 session_type_to_fs_stream_direction(
1429 stream
->session
->type
& ((active
) ?
1430 ~PURPLE_MEDIA_SEND_VIDEO
:
1431 PURPLE_MEDIA_VIDEO
)), NULL
);
1438 init_conference(PurpleMediaBackendFs2
*self
)
1440 PurpleMediaBackendFs2Private
*priv
=
1441 purple_media_backend_fs2_get_instance_private(self
);
1442 GstElement
*pipeline
;
1445 GKeyFile
*default_props
;
1447 priv
->conference
= FS_CONFERENCE(
1448 gst_element_factory_make(priv
->conference_type
, NULL
));
1450 if (priv
->conference
== NULL
) {
1451 purple_debug_error("backend-fs2", "Conference == NULL\n");
1455 if (purple_account_get_silence_suppression(
1456 purple_media_get_account(priv
->media
)))
1457 priv
->silence_threshold
= purple_prefs_get_int(
1458 "/purple/media/audio/silence_threshold") / 100.0;
1460 priv
->silence_threshold
= 0;
1462 pipeline
= purple_media_manager_get_pipeline(
1463 purple_media_get_manager(priv
->media
));
1465 if (pipeline
== NULL
) {
1466 purple_debug_error("backend-fs2",
1467 "Couldn't retrieve pipeline.\n");
1471 name
= g_strdup_printf("conf_%p", priv
->conference
);
1472 priv
->confbin
= gst_bin_new(name
);
1473 if (priv
->confbin
== NULL
) {
1474 purple_debug_error("backend-fs2",
1475 "Couldn't create confbin.\n");
1481 bus
= gst_pipeline_get_bus(GST_PIPELINE(pipeline
));
1483 purple_debug_error("backend-fs2",
1484 "Couldn't get the pipeline's bus.\n");
1488 default_props
= fs_utils_get_default_element_properties(GST_ELEMENT(priv
->conference
));
1489 if (default_props
!= NULL
) {
1490 priv
->notifier
= fs_element_added_notifier_new();
1491 fs_element_added_notifier_add(priv
->notifier
,
1492 GST_BIN(priv
->confbin
));
1493 fs_element_added_notifier_set_properties_from_keyfile(priv
->notifier
, default_props
);
1496 g_signal_connect(G_OBJECT(bus
), "message",
1497 G_CALLBACK(gst_bus_cb
), self
);
1498 gst_object_unref(bus
);
1500 if (!gst_bin_add(GST_BIN(pipeline
),
1501 GST_ELEMENT(priv
->confbin
))) {
1502 purple_debug_error("backend-fs2", "Couldn't add confbin "
1503 "element to the pipeline\n");
1507 if (!gst_bin_add(GST_BIN(priv
->confbin
),
1508 GST_ELEMENT(priv
->conference
))) {
1509 purple_debug_error("backend-fs2", "Couldn't add conference "
1510 "element to the confbin\n");
1514 if (gst_element_set_state(GST_ELEMENT(priv
->confbin
),
1515 GST_STATE_PLAYING
) == GST_STATE_CHANGE_FAILURE
) {
1516 purple_debug_error("backend-fs2",
1517 "Failed to start conference.\n");
1525 create_src(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
,
1526 PurpleMediaSessionType type
)
1528 PurpleMediaBackendFs2Private
*priv
=
1529 purple_media_backend_fs2_get_instance_private(self
);
1530 PurpleMediaBackendFs2Session
*session
;
1531 PurpleMediaSessionType session_type
;
1532 FsMediaType media_type
= session_type_to_fs_media_type(type
);
1533 FsStreamDirection type_direction
=
1534 session_type_to_fs_stream_direction(type
);
1536 GstPad
*sinkpad
, *srcpad
;
1537 GstPad
*ghost
= NULL
;
1539 if ((type_direction
& FS_DIRECTION_SEND
) == 0)
1542 session_type
= session_type_from_fs(
1543 media_type
, FS_DIRECTION_SEND
);
1544 src
= purple_media_manager_get_element(
1545 purple_media_get_manager(priv
->media
),
1546 session_type
, priv
->media
, sess_id
, NULL
);
1548 if (!GST_IS_ELEMENT(src
)) {
1549 purple_debug_error("backend-fs2",
1550 "Error creating src for session %s\n",
1555 session
= get_session(self
, sess_id
);
1557 if (session
== NULL
) {
1558 purple_debug_warning("backend-fs2",
1559 "purple_media_set_src: trying to set"
1560 " src on non-existent session\n");
1565 gst_object_unref(session
->src
);
1568 gst_element_set_locked_state(session
->src
, TRUE
);
1570 session
->tee
= gst_element_factory_make("tee", NULL
);
1571 gst_bin_add(GST_BIN(priv
->confbin
), session
->tee
);
1573 /* This supposedly isn't necessary, but it silences some warnings */
1574 if (GST_ELEMENT_PARENT(priv
->confbin
)
1575 == GST_ELEMENT_PARENT(session
->src
)) {
1576 GstPad
*pad
= gst_element_get_static_pad(session
->tee
, "sink");
1577 ghost
= gst_ghost_pad_new(NULL
, pad
);
1578 gst_object_unref(pad
);
1579 gst_pad_set_active(ghost
, TRUE
);
1580 gst_element_add_pad(priv
->confbin
, ghost
);
1583 gst_element_set_state(session
->tee
, GST_STATE_PLAYING
);
1584 gst_element_link(session
->src
, priv
->confbin
);
1586 session
->srcpad
= gst_pad_get_peer(ghost
);
1588 g_object_get(session
->session
, "sink-pad", &sinkpad
, NULL
);
1589 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
1590 gchar
*name
= g_strdup_printf("volume_%s", session
->id
);
1592 GstElement
*volume
= gst_element_factory_make("volume", name
);
1593 double input_volume
= purple_prefs_get_int(
1594 "/purple/media/audio/volume/input")/10.0;
1596 name
= g_strdup_printf("sendlevel_%s", session
->id
);
1597 level
= gst_element_factory_make("level", name
);
1599 session
->srcvalve
= gst_element_factory_make("valve", NULL
);
1600 gst_bin_add(GST_BIN(priv
->confbin
), volume
);
1601 gst_bin_add(GST_BIN(priv
->confbin
), level
);
1602 gst_bin_add(GST_BIN(priv
->confbin
), session
->srcvalve
);
1603 gst_element_set_state(level
, GST_STATE_PLAYING
);
1604 gst_element_set_state(volume
, GST_STATE_PLAYING
);
1605 gst_element_set_state(session
->srcvalve
, GST_STATE_PLAYING
);
1606 gst_element_link(level
, session
->srcvalve
);
1607 gst_element_link(volume
, level
);
1608 gst_element_link(session
->tee
, volume
);
1609 srcpad
= gst_element_get_static_pad(session
->srcvalve
, "src");
1610 g_object_set(volume
, "volume", input_volume
, NULL
);
1612 srcpad
= gst_element_get_request_pad(session
->tee
, "src_%u");
1615 purple_debug_info("backend-fs2", "connecting pad: %s\n",
1616 gst_pad_link(srcpad
, sinkpad
) == GST_PAD_LINK_OK
1617 ? "success" : "failure");
1618 gst_element_set_locked_state(session
->src
, FALSE
);
1619 gst_object_unref(session
->src
);
1620 gst_object_unref(sinkpad
);
1622 purple_media_manager_create_output_window(purple_media_get_manager(
1623 priv
->media
), priv
->media
, sess_id
, NULL
);
1625 purple_debug_info("backend-fs2", "create_src: setting source "
1626 "state to GST_STATE_PLAYING - it may hang here on win32\n");
1627 gst_element_set_state(session
->src
, GST_STATE_PLAYING
);
1628 purple_debug_info("backend-fs2", "create_src: state set\n");
1634 create_session(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
,
1635 PurpleMediaSessionType type
, gboolean initiator
,
1636 const gchar
*transmitter
)
1638 PurpleMediaBackendFs2Private
*priv
=
1639 purple_media_backend_fs2_get_instance_private(self
);
1640 PurpleMediaBackendFs2Session
*session
;
1642 GList
*codec_conf
= NULL
;
1643 gchar
*filename
= NULL
;
1645 session
= g_new0(PurpleMediaBackendFs2Session
, 1);
1647 session
->session
= fs_conference_new_session(priv
->conference
,
1648 session_type_to_fs_media_type(type
), &err
);
1650 #ifdef HAVE_MEDIA_APPLICATION
1651 if (type
== PURPLE_MEDIA_APPLICATION
) {
1653 GObject
*rtpsession
= NULL
;
1655 caps
= gst_caps_new_empty_simple ("application/octet-stream");
1656 fs_session_set_allowed_caps (session
->session
, caps
, caps
, NULL
);
1657 gst_caps_unref (caps
);
1658 g_object_get (session
->session
, "internal-session", &rtpsession
, NULL
);
1660 g_object_set (rtpsession
, "probation", 0, NULL
);
1661 g_object_unref (rtpsession
);
1666 purple_media_error(priv
->media
,
1667 _("Error creating session: %s"),
1674 filename
= g_build_filename(purple_config_dir(), "fs-codec.conf", NULL
);
1675 codec_conf
= fs_codec_list_from_keyfile(filename
, &err
);
1679 if (err
->code
== G_KEY_FILE_ERROR_NOT_FOUND
)
1680 purple_debug_info("backend-fs2", "Couldn't read "
1681 "fs-codec.conf: %s\n",
1684 purple_debug_error("backend-fs2", "Error reading "
1685 "fs-codec.conf: %s\n",
1689 purple_debug_info("backend-fs2",
1690 "Loading default codec conf instead\n");
1691 codec_conf
= fs_utils_get_default_codec_preferences(
1692 GST_ELEMENT(priv
->conference
));
1695 fs_session_set_codec_preferences(session
->session
, codec_conf
, NULL
);
1696 fs_codec_list_destroy(codec_conf
);
1699 * Removes a 5-7 second delay before
1700 * receiving the src-pad-added signal.
1701 * Only works for non-multicast FsRtpSessions.
1703 if (!purple_strequal(transmitter
, "multicast"))
1704 g_object_set(G_OBJECT(session
->session
),
1705 "no-rtcp-timeout", 0, NULL
);
1707 session
->id
= g_strdup(sess_id
);
1708 session
->backend
= self
;
1709 session
->type
= type
;
1711 if (!priv
->sessions
) {
1712 purple_debug_info("backend-fs2",
1713 "Creating hash table for sessions\n");
1714 priv
->sessions
= g_hash_table_new_full(g_str_hash
, g_str_equal
,
1718 g_hash_table_insert(priv
->sessions
, g_strdup(session
->id
), session
);
1720 if (!create_src(self
, sess_id
, type
)) {
1721 purple_debug_info("backend-fs2", "Error creating the src\n");
1729 free_session(PurpleMediaBackendFs2Session
*session
)
1731 g_free(session
->id
);
1736 create_participant(PurpleMediaBackendFs2
*self
, const gchar
*name
)
1738 PurpleMediaBackendFs2Private
*priv
=
1739 purple_media_backend_fs2_get_instance_private(self
);
1740 FsParticipant
*participant
;
1743 participant
= fs_conference_new_participant(
1744 priv
->conference
, &err
);
1747 purple_debug_error("backend-fs2",
1748 "Error creating participant: %s\n",
1754 g_object_set_data_full(G_OBJECT(participant
), "purple-name",
1755 g_strdup(name
), g_free
);
1757 if (g_object_class_find_property(G_OBJECT_GET_CLASS(participant
),
1759 g_object_set(participant
, "cname", name
, NULL
);
1762 if (!priv
->participants
) {
1763 purple_debug_info("backend-fs2",
1764 "Creating hash table for participants\n");
1765 priv
->participants
= g_hash_table_new_full(g_str_hash
,
1766 g_str_equal
, g_free
, g_object_unref
);
1769 g_hash_table_insert(priv
->participants
, g_strdup(name
), participant
);
1775 src_pad_added_cb_cb(PurpleMediaBackendFs2Stream
*stream
)
1777 PurpleMediaBackendFs2Private
*priv
;
1779 g_return_val_if_fail(stream
!= NULL
, FALSE
);
1781 priv
= purple_media_backend_fs2_get_instance_private(
1782 stream
->session
->backend
);
1783 stream
->connected_cb_id
= 0;
1785 purple_media_manager_create_output_window(
1786 purple_media_get_manager(priv
->media
), priv
->media
,
1787 stream
->session
->id
, stream
->participant
);
1789 g_signal_emit_by_name(priv
->media
, "state-changed",
1790 PURPLE_MEDIA_STATE_CONNECTED
,
1791 stream
->session
->id
, stream
->participant
);
1796 src_pad_added_cb(FsStream
*fsstream
, GstPad
*srcpad
,
1797 FsCodec
*codec
, PurpleMediaBackendFs2Stream
*stream
)
1799 PurpleMediaBackendFs2Private
*priv
;
1802 g_return_if_fail(FS_IS_STREAM(fsstream
));
1803 g_return_if_fail(stream
!= NULL
);
1805 priv
= purple_media_backend_fs2_get_instance_private(
1806 stream
->session
->backend
);
1808 if (stream
->src
== NULL
) {
1809 GstElement
*sink
= NULL
;
1811 if (codec
->media_type
== FS_MEDIA_TYPE_AUDIO
) {
1812 double output_volume
= purple_prefs_get_int(
1813 "/purple/media/audio/volume/output")/10.0;
1815 * Should this instead be:
1816 * audioconvert ! audioresample ! liveadder !
1817 * audioresample ! audioconvert ! realsink
1819 stream
->queue
= gst_element_factory_make("queue", NULL
);
1820 stream
->volume
= gst_element_factory_make("volume", NULL
);
1821 g_object_set(stream
->volume
, "volume", output_volume
, NULL
);
1822 stream
->level
= gst_element_factory_make("level", NULL
);
1823 stream
->src
= gst_element_factory_make("liveadder", NULL
);
1824 sink
= purple_media_manager_get_element(
1825 purple_media_get_manager(priv
->media
),
1826 PURPLE_MEDIA_RECV_AUDIO
, priv
->media
,
1827 stream
->session
->id
,
1828 stream
->participant
);
1829 gst_bin_add(GST_BIN(priv
->confbin
), stream
->queue
);
1830 gst_bin_add(GST_BIN(priv
->confbin
), stream
->volume
);
1831 gst_bin_add(GST_BIN(priv
->confbin
), stream
->level
);
1832 gst_bin_add(GST_BIN(priv
->confbin
), sink
);
1833 gst_element_set_state(sink
, GST_STATE_PLAYING
);
1834 gst_element_set_state(stream
->level
, GST_STATE_PLAYING
);
1835 gst_element_set_state(stream
->volume
, GST_STATE_PLAYING
);
1836 gst_element_set_state(stream
->queue
, GST_STATE_PLAYING
);
1837 gst_element_link(stream
->level
, sink
);
1838 gst_element_link(stream
->volume
, stream
->level
);
1839 gst_element_link(stream
->queue
, stream
->volume
);
1840 sink
= stream
->queue
;
1841 } else if (codec
->media_type
== FS_MEDIA_TYPE_VIDEO
) {
1842 stream
->src
= gst_element_factory_make("funnel", NULL
);
1843 sink
= gst_element_factory_make("fakesink", NULL
);
1844 g_object_set(G_OBJECT(sink
), "async", FALSE
, NULL
);
1845 gst_bin_add(GST_BIN(priv
->confbin
), sink
);
1846 gst_element_set_state(sink
, GST_STATE_PLAYING
);
1847 stream
->fakesink
= sink
;
1848 #ifdef HAVE_MEDIA_APPLICATION
1849 } else if (codec
->media_type
== FS_MEDIA_TYPE_APPLICATION
) {
1850 stream
->src
= gst_element_factory_make("funnel", NULL
);
1851 sink
= purple_media_manager_get_element(
1852 purple_media_get_manager(priv
->media
),
1853 PURPLE_MEDIA_RECV_APPLICATION
, priv
->media
,
1854 stream
->session
->id
,
1855 stream
->participant
);
1856 gst_bin_add(GST_BIN(priv
->confbin
), sink
);
1857 gst_element_set_state(sink
, GST_STATE_PLAYING
);
1860 stream
->tee
= gst_element_factory_make("tee", NULL
);
1861 gst_bin_add_many(GST_BIN(priv
->confbin
),
1862 stream
->src
, stream
->tee
, NULL
);
1863 gst_element_set_state(stream
->tee
, GST_STATE_PLAYING
);
1864 gst_element_set_state(stream
->src
, GST_STATE_PLAYING
);
1865 gst_element_link_many(stream
->src
, stream
->tee
, sink
, NULL
);
1868 sinkpad
= gst_element_get_request_pad(stream
->src
, "sink_%u");
1869 gst_pad_link(srcpad
, sinkpad
);
1870 gst_object_unref(sinkpad
);
1872 stream
->connected_cb_id
= g_timeout_add(0,
1873 (GSourceFunc
)src_pad_added_cb_cb
, stream
);
1877 create_stream(PurpleMediaBackendFs2
*self
,
1878 const gchar
*sess_id
, const gchar
*who
,
1879 PurpleMediaSessionType type
, gboolean initiator
,
1880 const gchar
*transmitter
,
1881 guint num_params
, GParameter
*params
)
1883 PurpleMediaBackendFs2Private
*priv
=
1884 purple_media_backend_fs2_get_instance_private(self
);
1886 FsStream
*fsstream
= NULL
;
1887 const gchar
*stun_ip
= purple_network_get_stun_ip();
1888 const gchar
*turn_ip
= purple_network_get_turn_ip();
1889 guint _num_params
= num_params
;
1890 GParameter
*_params
;
1891 FsStreamDirection type_direction
=
1892 session_type_to_fs_stream_direction(type
);
1893 PurpleMediaBackendFs2Session
*session
;
1894 PurpleMediaBackendFs2Stream
*stream
;
1895 FsParticipant
*participant
;
1896 /* check if the protocol has already specified a relay-info
1897 we need to do this to allow them to override when using non-standard
1898 TURN modes, like Google f.ex. */
1899 gboolean got_turn_from_protocol
= FALSE
;
1901 GPtrArray
*relay_info
= g_ptr_array_new_full (1, (GDestroyNotify
) gst_structure_free
);
1904 session
= get_session(self
, sess_id
);
1906 if (session
== NULL
) {
1907 purple_debug_error("backend-fs2",
1908 "Couldn't find session to create stream.\n");
1912 participant
= get_participant(self
, who
);
1914 if (participant
== NULL
) {
1915 purple_debug_error("backend-fs2", "Couldn't find "
1916 "participant to create stream.\n");
1920 fsstream
= fs_session_new_stream(session
->session
, participant
,
1921 initiator
== TRUE
? type_direction
:
1922 (type_direction
& FS_DIRECTION_RECV
), &err
);
1924 if (fsstream
== NULL
) {
1926 purple_debug_error("backend-fs2", "Error creating stream: %s\n",
1927 err
->message
? err
->message
: "NULL");
1930 purple_debug_error("backend-fs2",
1931 "Error creating stream\n");
1935 for (i
= 0 ; i
< num_params
; i
++) {
1936 if (purple_strequal(params
[i
].name
, "relay-info")) {
1937 got_turn_from_protocol
= TRUE
;
1942 _params
= g_new0(GParameter
, num_params
+ 3);
1943 memcpy(_params
, params
, sizeof(GParameter
) * num_params
);
1945 /* set the controlling mode parameter */
1946 _params
[_num_params
].name
= "controlling-mode";
1947 g_value_init(&_params
[_num_params
].value
, G_TYPE_BOOLEAN
);
1948 g_value_set_boolean(&_params
[_num_params
].value
, initiator
);
1952 purple_debug_info("backend-fs2",
1953 "Setting stun-ip on new stream: %s\n", stun_ip
);
1955 _params
[_num_params
].name
= "stun-ip";
1956 g_value_init(&_params
[_num_params
].value
, G_TYPE_STRING
);
1957 g_value_set_string(&_params
[_num_params
].value
, stun_ip
);
1961 if (turn_ip
&& purple_strequal("nice", transmitter
) && !got_turn_from_protocol
) {
1963 const gchar
*username
= purple_prefs_get_string(
1964 "/purple/network/turn_username");
1965 const gchar
*password
= purple_prefs_get_string(
1966 "/purple/network/turn_password");
1969 port
= purple_prefs_get_int("/purple/network/turn_port");
1971 g_ptr_array_add (relay_info
,
1972 gst_structure_new ("relay-info",
1973 "ip", G_TYPE_STRING
, turn_ip
,
1974 "port", G_TYPE_UINT
, port
,
1975 "username", G_TYPE_STRING
, username
,
1976 "password", G_TYPE_STRING
, password
,
1977 "relay-type", G_TYPE_STRING
, "udp",
1982 port
= purple_prefs_get_int("/purple/network/turn_port_tcp");
1984 g_ptr_array_add (relay_info
,
1985 gst_structure_new ("relay-info",
1986 "ip", G_TYPE_STRING
, turn_ip
,
1987 "port", G_TYPE_UINT
, port
,
1988 "username", G_TYPE_STRING
, username
,
1989 "password", G_TYPE_STRING
, password
,
1990 "relay-type", G_TYPE_STRING
, "tcp",
1994 /* TURN over SSL is only supported by libnice for Google's "psuedo" SSL mode
1997 purple_debug_info("backend-fs2",
1998 "Setting relay-info on new stream\n");
1999 _params
[_num_params
].name
= "relay-info";
2000 g_value_init(&_params
[_num_params
].value
, G_TYPE_PTR_ARRAY
);
2001 g_value_set_boxed(&_params
[_num_params
].value
, relay_info
);
2005 ret
= fs_stream_set_transmitter(fsstream
, transmitter
,
2006 _params
, _num_params
, &err
);
2007 for (i
= 0 ; i
< _num_params
; i
++)
2008 g_value_unset (&_params
[i
].value
);
2011 g_ptr_array_unref (relay_info
);
2013 purple_debug_error("backend-fs2",
2014 "Could not set transmitter %s: %s.\n",
2015 transmitter
, err
? err
->message
: NULL
);
2016 g_clear_error(&err
);
2020 stream
= g_new0(PurpleMediaBackendFs2Stream
, 1);
2021 stream
->participant
= g_strdup(who
);
2022 stream
->session
= session
;
2023 stream
->stream
= fsstream
;
2024 stream
->supports_add
= purple_strequal(transmitter
, "nice");
2026 priv
->streams
= g_list_append(priv
->streams
, stream
);
2028 g_signal_connect(G_OBJECT(fsstream
), "src-pad-added",
2029 G_CALLBACK(src_pad_added_cb
), stream
);
2035 free_stream(PurpleMediaBackendFs2Stream
*stream
)
2037 /* Remove the connected_cb timeout */
2038 if (stream
->connected_cb_id
!= 0)
2039 g_source_remove(stream
->connected_cb_id
);
2041 g_free(stream
->participant
);
2043 if (stream
->local_candidates
)
2044 fs_candidate_list_destroy(stream
->local_candidates
);
2046 if (stream
->remote_candidates
)
2047 fs_candidate_list_destroy(stream
->remote_candidates
);
2053 purple_media_backend_fs2_add_stream(PurpleMediaBackend
*self
,
2054 const gchar
*sess_id
, const gchar
*who
,
2055 PurpleMediaSessionType type
, gboolean initiator
,
2056 const gchar
*transmitter
,
2057 guint num_params
, GParameter
*params
)
2059 PurpleMediaBackendFs2
*backend
= PURPLE_MEDIA_BACKEND_FS2(self
);
2060 PurpleMediaBackendFs2Private
*priv
=
2061 purple_media_backend_fs2_get_instance_private(backend
);
2062 PurpleMediaBackendFs2Stream
*stream
;
2064 if (priv
->conference
== NULL
&& !init_conference(backend
)) {
2065 purple_debug_error("backend-fs2",
2066 "Error initializing the conference.\n");
2070 if (get_session(backend
, sess_id
) == NULL
&&
2071 !create_session(backend
, sess_id
, type
,
2072 initiator
, transmitter
)) {
2073 purple_debug_error("backend-fs2",
2074 "Error creating the session.\n");
2078 if (get_participant(backend
, who
) == NULL
&&
2079 !create_participant(backend
, who
)) {
2080 purple_debug_error("backend-fs2",
2081 "Error creating the participant.\n");
2085 stream
= get_stream(backend
, sess_id
, who
);
2087 if (stream
!= NULL
) {
2088 FsStreamDirection type_direction
=
2089 session_type_to_fs_stream_direction(type
);
2091 if (session_type_to_fs_stream_direction(
2092 stream
->session
->type
) != type_direction
) {
2093 /* change direction */
2094 g_object_set(stream
->stream
, "direction",
2095 type_direction
, NULL
);
2097 } else if (!create_stream(backend
, sess_id
, who
, type
,
2098 initiator
, transmitter
, num_params
, params
)) {
2099 purple_debug_error("backend-fs2",
2100 "Error creating the stream.\n");
2108 purple_media_backend_fs2_add_remote_candidates(PurpleMediaBackend
*self
,
2109 const gchar
*sess_id
, const gchar
*participant
,
2110 GList
*remote_candidates
)
2112 PurpleMediaBackendFs2Private
*priv
;
2113 PurpleMediaBackendFs2Stream
*stream
;
2116 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2118 priv
= purple_media_backend_fs2_get_instance_private(
2119 PURPLE_MEDIA_BACKEND_FS2(self
));
2120 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2121 sess_id
, participant
);
2123 if (stream
== NULL
) {
2124 purple_debug_error("backend-fs2",
2125 "purple_media_add_remote_candidates: "
2126 "couldn't find stream %s %s.\n",
2127 sess_id
? sess_id
: "(null)",
2128 participant
? participant
: "(null)");
2132 stream
->remote_candidates
= g_list_concat(stream
->remote_candidates
,
2133 candidate_list_to_fs(remote_candidates
));
2135 if (purple_media_is_initiator(priv
->media
, sess_id
, participant
) ||
2136 purple_media_accepted(
2137 priv
->media
, sess_id
, participant
)) {
2139 if (stream
->supports_add
)
2140 fs_stream_add_remote_candidates(stream
->stream
,
2141 stream
->remote_candidates
, &err
);
2143 fs_stream_force_remote_candidates(stream
->stream
,
2144 stream
->remote_candidates
, &err
);
2147 purple_debug_error("backend-fs2", "Error adding remote"
2148 " candidates: %s\n", err
->message
);
2155 purple_media_backend_fs2_codecs_ready(PurpleMediaBackend
*self
,
2156 const gchar
*sess_id
)
2158 PurpleMediaBackendFs2Private
*priv
;
2159 gboolean ret
= FALSE
;
2161 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2163 priv
= purple_media_backend_fs2_get_instance_private(
2164 PURPLE_MEDIA_BACKEND_FS2(self
));
2166 if (sess_id
!= NULL
) {
2167 PurpleMediaBackendFs2Session
*session
= get_session(
2168 PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2170 if (session
== NULL
)
2173 if (session
->type
& (PURPLE_MEDIA_SEND_AUDIO
|
2174 #ifdef HAVE_MEDIA_APPLICATION
2175 PURPLE_MEDIA_SEND_APPLICATION
|
2177 PURPLE_MEDIA_SEND_VIDEO
)) {
2179 GList
*codecs
= NULL
;
2181 g_object_get(session
->session
,
2182 "codecs", &codecs
, NULL
);
2184 fs_codec_list_destroy (codecs
);
2190 GList
*values
= g_hash_table_get_values(priv
->sessions
);
2192 for (; values
; values
= g_list_delete_link(values
, values
)) {
2193 PurpleMediaBackendFs2Session
*session
= values
->data
;
2195 if (session
->type
& (PURPLE_MEDIA_SEND_AUDIO
|
2196 #ifdef HAVE_MEDIA_APPLICATION
2197 PURPLE_MEDIA_SEND_APPLICATION
|
2199 PURPLE_MEDIA_SEND_VIDEO
)) {
2201 GList
*codecs
= NULL
;
2203 g_object_get(session
->session
,
2204 "codecs", &codecs
, NULL
);
2206 fs_codec_list_destroy (codecs
);
2217 g_list_free(values
);
2224 purple_media_backend_fs2_get_codecs(PurpleMediaBackend
*self
,
2225 const gchar
*sess_id
)
2227 PurpleMediaBackendFs2Session
*session
;
2231 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
2233 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2235 if (session
== NULL
)
2238 g_object_get(G_OBJECT(session
->session
),
2239 "codecs", &fscodecs
, NULL
);
2240 codecs
= codec_list_from_fs(fscodecs
);
2241 fs_codec_list_destroy(fscodecs
);
2247 purple_media_backend_fs2_get_local_candidates(PurpleMediaBackend
*self
,
2248 const gchar
*sess_id
, const gchar
*participant
)
2250 PurpleMediaBackendFs2Stream
*stream
;
2251 GList
*candidates
= NULL
;
2253 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
2255 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2256 sess_id
, participant
);
2259 candidates
= candidate_list_from_fs(
2260 stream
->local_candidates
);
2265 purple_media_backend_fs2_set_remote_codecs(PurpleMediaBackend
*self
,
2266 const gchar
*sess_id
, const gchar
*participant
,
2269 PurpleMediaBackendFs2Stream
*stream
;
2273 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2274 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2275 sess_id
, participant
);
2280 fscodecs
= codec_list_to_fs(codecs
);
2281 fs_stream_set_remote_codecs(stream
->stream
, fscodecs
, &err
);
2282 fs_codec_list_destroy(fscodecs
);
2285 purple_debug_error("backend-fs2",
2286 "Error setting remote codecs: %s\n",
2295 static GstStructure
*
2296 create_fs2_srtp_structure(const gchar
*cipher
, const gchar
*auth
,
2297 const gchar
*key
, gsize key_len
)
2299 GstStructure
*result
;
2303 buffer
= gst_buffer_new_allocate(NULL
, key_len
, NULL
);
2304 gst_buffer_map(buffer
, &info
, GST_MAP_WRITE
);
2305 memcpy(info
.data
, key
, key_len
);
2306 gst_buffer_unmap(buffer
, &info
);
2308 result
= gst_structure_new("FarstreamSRTP",
2309 "cipher", G_TYPE_STRING
, cipher
,
2310 "auth", G_TYPE_STRING
, auth
,
2311 "key", GST_TYPE_BUFFER
, buffer
,
2313 gst_buffer_unref(buffer
);
2319 purple_media_backend_fs2_set_encryption_parameters (PurpleMediaBackend
*self
,
2320 const gchar
*sess_id
, const gchar
*cipher
, const gchar
*auth
,
2321 const gchar
*key
, gsize key_len
)
2323 PurpleMediaBackendFs2Session
*session
;
2328 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2330 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2334 srtp
= create_fs2_srtp_structure(cipher
, auth
, key
, key_len
);
2338 result
= fs_session_set_encryption_parameters(session
->session
, srtp
,
2341 purple_debug_error("backend-fs2",
2342 "Error setting encryption parameters: %s\n", err
->message
);
2346 gst_structure_free(srtp
);
2351 purple_media_backend_fs2_set_decryption_parameters (PurpleMediaBackend
*self
,
2352 const gchar
*sess_id
, const gchar
*participant
,
2353 const gchar
*cipher
, const gchar
*auth
,
2354 const gchar
*key
, gsize key_len
)
2356 PurpleMediaBackendFs2Stream
*stream
;
2361 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2363 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
,
2368 srtp
= create_fs2_srtp_structure(cipher
, auth
, key
, key_len
);
2372 result
= fs_stream_set_decryption_parameters(stream
->stream
, srtp
,
2375 purple_debug_error("backend-fs2",
2376 "Error setting decryption parameters: %s\n", err
->message
);
2380 gst_structure_free(srtp
);
2385 purple_media_backend_fs2_set_send_codec(PurpleMediaBackend
*self
,
2386 const gchar
*sess_id
, PurpleMediaCodec
*codec
)
2388 PurpleMediaBackendFs2Session
*session
;
2392 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2394 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2396 if (session
== NULL
)
2399 fscodec
= codec_to_fs(codec
);
2400 fs_session_set_send_codec(session
->session
, fscodec
, &err
);
2401 fs_codec_destroy(fscodec
);
2404 purple_debug_error("media", "Error setting send codec\n");
2412 static const gchar
**
2413 purple_media_backend_fs2_get_available_params(void)
2415 static const gchar
*supported_params
[] = {
2416 "sdes-cname", "sdes-email", "sdes-location", "sdes-name", "sdes-note",
2417 "sdes-phone", "sdes-tool", NULL
2420 return supported_params
;
2424 param_to_sdes_type(const gchar
*param
)
2426 const gchar
**supported
= purple_media_backend_fs2_get_available_params();
2427 static const gchar
*sdes_types
[] = {
2428 "cname", "email", "location", "name", "note", "phone", "tool", NULL
2432 for (i
= 0; supported
[i
] != NULL
; ++i
) {
2433 if (purple_strequal(param
, supported
[i
])) {
2434 return sdes_types
[i
];
2442 purple_media_backend_fs2_set_params(PurpleMediaBackend
*self
,
2443 guint num_params
, GParameter
*params
)
2445 PurpleMediaBackendFs2Private
*priv
;
2449 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2451 priv
= purple_media_backend_fs2_get_instance_private(
2452 PURPLE_MEDIA_BACKEND_FS2(self
));
2454 if (priv
->conference
== NULL
&&
2455 !init_conference(PURPLE_MEDIA_BACKEND_FS2(self
))) {
2456 purple_debug_error("backend-fs2",
2457 "Error initializing the conference.\n");
2461 g_object_get(G_OBJECT(priv
->conference
), "sdes", &sdes
, NULL
);
2463 for (i
= 0; i
!= num_params
; ++i
) {
2464 const gchar
*sdes_type
= param_to_sdes_type(params
[i
].name
);
2468 gst_structure_set(sdes
, sdes_type
,
2469 G_TYPE_STRING
, g_value_get_string(¶ms
[i
].value
),
2473 g_object_set(G_OBJECT(priv
->conference
), "sdes", sdes
, NULL
);
2474 gst_structure_free(sdes
);
2477 send_dtmf_callback(gpointer userdata
)
2479 FsSession
*session
= userdata
;
2481 fs_session_stop_telephony_event(session
);
2486 purple_media_backend_fs2_send_dtmf(PurpleMediaBackend
*self
,
2487 const gchar
*sess_id
, gchar dtmf
, guint8 volume
,
2490 PurpleMediaBackendFs2Session
*session
;
2493 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2495 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
2496 if (session
== NULL
)
2499 /* Convert DTMF char into FsDTMFEvent enum */
2501 case '0': event
= FS_DTMF_EVENT_0
; break;
2502 case '1': event
= FS_DTMF_EVENT_1
; break;
2503 case '2': event
= FS_DTMF_EVENT_2
; break;
2504 case '3': event
= FS_DTMF_EVENT_3
; break;
2505 case '4': event
= FS_DTMF_EVENT_4
; break;
2506 case '5': event
= FS_DTMF_EVENT_5
; break;
2507 case '6': event
= FS_DTMF_EVENT_6
; break;
2508 case '7': event
= FS_DTMF_EVENT_7
; break;
2509 case '8': event
= FS_DTMF_EVENT_8
; break;
2510 case '9': event
= FS_DTMF_EVENT_9
; break;
2511 case '*': event
= FS_DTMF_EVENT_STAR
; break;
2512 case '#': event
= FS_DTMF_EVENT_POUND
; break;
2513 case 'A': event
= FS_DTMF_EVENT_A
; break;
2514 case 'B': event
= FS_DTMF_EVENT_B
; break;
2515 case 'C': event
= FS_DTMF_EVENT_C
; break;
2516 case 'D': event
= FS_DTMF_EVENT_D
; break;
2521 if (!fs_session_start_telephony_event(session
->session
,
2526 if (duration
<= 50) {
2527 fs_session_stop_telephony_event(session
->session
);
2529 g_timeout_add(duration
, send_dtmf_callback
,
2537 purple_media_backend_fs2_get_type(void)
2544 purple_media_backend_fs2_get_src(PurpleMediaBackendFs2
*self
,
2545 const gchar
*sess_id
)
2548 PurpleMediaBackendFs2Session
*session
= get_session(self
, sess_id
);
2549 return session
!= NULL
? session
->src
: NULL
;
2556 purple_media_backend_fs2_get_tee(PurpleMediaBackendFs2
*self
,
2557 const gchar
*sess_id
, const gchar
*who
)
2560 if (sess_id
!= NULL
&& who
== NULL
) {
2561 PurpleMediaBackendFs2Session
*session
=
2562 get_session(self
, sess_id
);
2563 return (session
!= NULL
) ? session
->tee
: NULL
;
2564 } else if (sess_id
!= NULL
&& who
!= NULL
) {
2565 PurpleMediaBackendFs2Stream
*stream
=
2566 get_stream(self
, sess_id
, who
);
2567 return (stream
!= NULL
) ? stream
->tee
: NULL
;
2571 g_return_val_if_reached(NULL
);
2575 purple_media_backend_fs2_set_input_volume(PurpleMediaBackendFs2
*self
,
2576 const gchar
*sess_id
, double level
)
2579 PurpleMediaBackendFs2Private
*priv
;
2582 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2584 priv
= purple_media_backend_fs2_get_instance_private(self
);
2586 purple_prefs_set_int("/purple/media/audio/volume/input", level
);
2588 if (sess_id
== NULL
)
2589 sessions
= g_hash_table_get_values(priv
->sessions
);
2591 sessions
= g_list_append(NULL
, get_session(self
, sess_id
));
2593 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
2594 PurpleMediaBackendFs2Session
*session
= sessions
->data
;
2596 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
2597 gchar
*name
= g_strdup_printf("volume_%s",
2599 GstElement
*volume
= gst_bin_get_by_name(
2600 GST_BIN(priv
->confbin
), name
);
2602 g_object_set(volume
, "volume", level
/10.0, NULL
);
2609 purple_media_backend_fs2_set_output_volume(PurpleMediaBackendFs2
*self
,
2610 const gchar
*sess_id
, const gchar
*who
, double level
)
2615 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2617 purple_prefs_set_int("/purple/media/audio/volume/output", level
);
2619 streams
= get_streams(self
, sess_id
, who
);
2621 for (; streams
; streams
= g_list_delete_link(streams
, streams
)) {
2622 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
2624 if (stream
->session
->type
& PURPLE_MEDIA_RECV_AUDIO
2625 && GST_IS_ELEMENT(stream
->volume
)) {
2626 g_object_set(stream
->volume
, "volume",
2635 purple_media_backend_fs2_set_send_rtcp_mux(PurpleMediaBackend
*self
,
2636 const gchar
*sess_id
, const gchar
*participant
,
2637 gboolean send_rtcp_mux
)
2639 PurpleMediaBackendFs2Stream
*stream
;
2641 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
2642 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
2643 sess_id
, participant
);
2645 if (stream
!= NULL
&&
2646 g_object_class_find_property (G_OBJECT_GET_CLASS (stream
->stream
),
2647 "send-rtcp-mux") != NULL
) {
2648 g_object_set (stream
->stream
, "send-rtcp-mux", send_rtcp_mux
, NULL
);