2 * @file backend-fs2.c Farsight 2 backend for media API
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
29 #include "backend-fs2.h"
32 #include "backend-iface.h"
35 #include "media-gst.h"
37 #include <gst/farsight/fs-conference-iface.h>
38 #include <gst/farsight/fs-element-added-notifier.h>
40 /** @copydoc _PurpleMediaBackendFs2Class */
41 typedef struct _PurpleMediaBackendFs2Class PurpleMediaBackendFs2Class
;
42 /** @copydoc _PurpleMediaBackendFs2Private */
43 typedef struct _PurpleMediaBackendFs2Private PurpleMediaBackendFs2Private
;
44 /** @copydoc _PurpleMediaBackendFs2Session */
45 typedef struct _PurpleMediaBackendFs2Session PurpleMediaBackendFs2Session
;
46 /** @copydoc _PurpleMediaBackendFs2Stream */
47 typedef struct _PurpleMediaBackendFs2Stream PurpleMediaBackendFs2Stream
;
49 #define PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj) \
50 (G_TYPE_INSTANCE_GET_PRIVATE((obj), \
51 PURPLE_TYPE_MEDIA_BACKEND_FS2, PurpleMediaBackendFs2Private))
53 static void purple_media_backend_iface_init(PurpleMediaBackendIface
*iface
);
56 gst_bus_cb(GstBus
*bus
, GstMessage
*msg
, PurpleMediaBackendFs2
*self
);
58 state_changed_cb(PurpleMedia
*media
, PurpleMediaState state
,
59 gchar
*sid
, gchar
*name
, PurpleMediaBackendFs2
*self
);
61 stream_info_cb(PurpleMedia
*media
, PurpleMediaInfoType type
,
62 gchar
*sid
, gchar
*name
, gboolean local
,
63 PurpleMediaBackendFs2
*self
);
65 static gboolean
purple_media_backend_fs2_add_stream(PurpleMediaBackend
*self
,
66 const gchar
*sess_id
, const gchar
*who
,
67 PurpleMediaSessionType type
, gboolean initiator
,
68 const gchar
*transmitter
,
69 guint num_params
, GParameter
*params
);
70 static void purple_media_backend_fs2_add_remote_candidates(
71 PurpleMediaBackend
*self
,
72 const gchar
*sess_id
, const gchar
*participant
,
73 GList
*remote_candidates
);
74 static gboolean
purple_media_backend_fs2_codecs_ready(PurpleMediaBackend
*self
,
75 const gchar
*sess_id
);
76 static GList
*purple_media_backend_fs2_get_codecs(PurpleMediaBackend
*self
,
77 const gchar
*sess_id
);
78 static GList
*purple_media_backend_fs2_get_local_candidates(
79 PurpleMediaBackend
*self
,
80 const gchar
*sess_id
, const gchar
*participant
);
81 static gboolean
purple_media_backend_fs2_set_remote_codecs(
82 PurpleMediaBackend
*self
,
83 const gchar
*sess_id
, const gchar
*participant
,
85 static gboolean
purple_media_backend_fs2_set_send_codec(
86 PurpleMediaBackend
*self
, const gchar
*sess_id
,
87 PurpleMediaCodec
*codec
);
89 struct _PurpleMediaBackendFs2Class
91 GObjectClass parent_class
;
94 struct _PurpleMediaBackendFs2
99 G_DEFINE_TYPE_WITH_CODE(PurpleMediaBackendFs2
, purple_media_backend_fs2
,
100 G_TYPE_OBJECT
, G_IMPLEMENT_INTERFACE(
101 PURPLE_TYPE_MEDIA_BACKEND
, purple_media_backend_iface_init
));
103 struct _PurpleMediaBackendFs2Stream
105 PurpleMediaBackendFs2Session
*session
;
114 GList
*local_candidates
;
115 GList
*remote_candidates
;
117 guint connected_cb_id
;
120 struct _PurpleMediaBackendFs2Session
122 PurpleMediaBackendFs2
*backend
;
129 PurpleMediaSessionType type
;
132 struct _PurpleMediaBackendFs2Private
136 FsConference
*conference
;
137 gchar
*conference_type
;
139 GHashTable
*sessions
;
140 GHashTable
*participants
;
147 PROP_CONFERENCE_TYPE
,
152 purple_media_backend_fs2_init(PurpleMediaBackendFs2
*self
)
157 purple_media_backend_fs2_dispose(GObject
*obj
)
159 PurpleMediaBackendFs2Private
*priv
=
160 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj
);
163 purple_debug_info("backend-fs2", "purple_media_backend_fs2_dispose\n");
166 GstElement
*pipeline
;
168 pipeline
= purple_media_manager_get_pipeline(
169 purple_media_get_manager(priv
->media
));
171 gst_element_set_locked_state(priv
->confbin
, TRUE
);
172 gst_element_set_state(GST_ELEMENT(priv
->confbin
),
177 gst_bin_remove(GST_BIN(pipeline
), priv
->confbin
);
178 bus
= gst_pipeline_get_bus(GST_PIPELINE(pipeline
));
179 g_signal_handlers_disconnect_matched(G_OBJECT(bus
),
180 G_SIGNAL_MATCH_FUNC
|
182 0, 0, 0, gst_bus_cb
, obj
);
183 gst_object_unref(bus
);
185 purple_debug_warning("backend-fs2", "Unable to "
186 "properly dispose the conference. "
187 "Couldn't get the pipeline.\n");
190 priv
->confbin
= NULL
;
191 priv
->conference
= NULL
;
195 if (priv
->sessions
) {
196 GList
*sessions
= g_hash_table_get_values(priv
->sessions
);
198 for (; sessions
; sessions
=
199 g_list_delete_link(sessions
, sessions
)) {
200 PurpleMediaBackendFs2Session
*session
=
203 if (session
->session
) {
204 g_object_unref(session
->session
);
205 session
->session
= NULL
;
210 if (priv
->participants
) {
211 GList
*participants
=
212 g_hash_table_get_values(priv
->participants
);
213 for (; participants
; participants
= g_list_delete_link(
214 participants
, participants
))
215 g_object_unref(participants
->data
);
216 priv
->participants
= NULL
;
219 for (iter
= priv
->streams
; iter
; iter
= g_list_next(iter
)) {
220 PurpleMediaBackendFs2Stream
*stream
= iter
->data
;
221 if (stream
->stream
) {
222 g_object_unref(stream
->stream
);
223 stream
->stream
= NULL
;
228 g_object_remove_weak_pointer(G_OBJECT(priv
->media
),
229 (gpointer
*)&priv
->media
);
233 G_OBJECT_CLASS(purple_media_backend_fs2_parent_class
)->dispose(obj
);
237 purple_media_backend_fs2_finalize(GObject
*obj
)
239 PurpleMediaBackendFs2Private
*priv
=
240 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(obj
);
242 purple_debug_info("backend-fs2", "purple_media_backend_fs2_finalize\n");
244 g_free(priv
->conference_type
);
246 for (; priv
->streams
; priv
->streams
=
247 g_list_delete_link(priv
->streams
, priv
->streams
)) {
248 PurpleMediaBackendFs2Stream
*stream
= priv
->streams
->data
;
250 /* Remove the connected_cb timeout */
251 if (stream
->connected_cb_id
!= 0)
252 purple_timeout_remove(stream
->connected_cb_id
);
254 g_free(stream
->participant
);
256 if (stream
->local_candidates
)
257 fs_candidate_list_destroy(stream
->local_candidates
);
259 if (stream
->remote_candidates
)
260 fs_candidate_list_destroy(stream
->remote_candidates
);
265 if (priv
->sessions
) {
266 GList
*sessions
= g_hash_table_get_values(priv
->sessions
);
268 for (; sessions
; sessions
=
269 g_list_delete_link(sessions
, sessions
)) {
270 PurpleMediaBackendFs2Session
*session
=
276 g_hash_table_destroy(priv
->sessions
);
279 G_OBJECT_CLASS(purple_media_backend_fs2_parent_class
)->finalize(obj
);
283 purple_media_backend_fs2_set_property(GObject
*object
, guint prop_id
,
284 const GValue
*value
, GParamSpec
*pspec
)
286 PurpleMediaBackendFs2Private
*priv
;
287 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object
));
289 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(object
);
292 case PROP_CONFERENCE_TYPE
:
293 priv
->conference_type
= g_value_dup_string(value
);
296 priv
->media
= g_value_get_object(value
);
298 if (priv
->media
== NULL
)
301 g_object_add_weak_pointer(G_OBJECT(priv
->media
),
302 (gpointer
*)&priv
->media
);
304 g_signal_connect(G_OBJECT(priv
->media
),
306 G_CALLBACK(state_changed_cb
),
307 PURPLE_MEDIA_BACKEND_FS2(object
));
308 g_signal_connect(G_OBJECT(priv
->media
), "stream-info",
309 G_CALLBACK(stream_info_cb
),
310 PURPLE_MEDIA_BACKEND_FS2(object
));
313 G_OBJECT_WARN_INVALID_PROPERTY_ID(
314 object
, prop_id
, pspec
);
320 purple_media_backend_fs2_get_property(GObject
*object
, guint prop_id
,
321 GValue
*value
, GParamSpec
*pspec
)
323 PurpleMediaBackendFs2Private
*priv
;
324 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(object
));
326 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(object
);
329 case PROP_CONFERENCE_TYPE
:
330 g_value_set_string(value
, priv
->conference_type
);
333 g_value_set_object(value
, priv
->media
);
336 G_OBJECT_WARN_INVALID_PROPERTY_ID(
337 object
, prop_id
, pspec
);
343 purple_media_backend_fs2_class_init(PurpleMediaBackendFs2Class
*klass
)
345 GObjectClass
*gobject_class
= (GObjectClass
*)klass
;
347 gobject_class
->dispose
= purple_media_backend_fs2_dispose
;
348 gobject_class
->finalize
= purple_media_backend_fs2_finalize
;
349 gobject_class
->set_property
= purple_media_backend_fs2_set_property
;
350 gobject_class
->get_property
= purple_media_backend_fs2_get_property
;
352 g_object_class_override_property(gobject_class
, PROP_CONFERENCE_TYPE
,
354 g_object_class_override_property(gobject_class
, PROP_MEDIA
, "media");
356 g_type_class_add_private(klass
, sizeof(PurpleMediaBackendFs2Private
));
360 purple_media_backend_iface_init(PurpleMediaBackendIface
*iface
)
362 iface
->add_stream
= purple_media_backend_fs2_add_stream
;
363 iface
->add_remote_candidates
=
364 purple_media_backend_fs2_add_remote_candidates
;
365 iface
->codecs_ready
= purple_media_backend_fs2_codecs_ready
;
366 iface
->get_codecs
= purple_media_backend_fs2_get_codecs
;
367 iface
->get_local_candidates
=
368 purple_media_backend_fs2_get_local_candidates
;
369 iface
->set_remote_codecs
= purple_media_backend_fs2_set_remote_codecs
;
370 iface
->set_send_codec
= purple_media_backend_fs2_set_send_codec
;
374 session_type_to_fs_media_type(PurpleMediaSessionType type
)
376 if (type
& PURPLE_MEDIA_AUDIO
)
377 return FS_MEDIA_TYPE_AUDIO
;
378 else if (type
& PURPLE_MEDIA_VIDEO
)
379 return FS_MEDIA_TYPE_VIDEO
;
384 static FsStreamDirection
385 session_type_to_fs_stream_direction(PurpleMediaSessionType type
)
387 if ((type
& PURPLE_MEDIA_AUDIO
) == PURPLE_MEDIA_AUDIO
||
388 (type
& PURPLE_MEDIA_VIDEO
) == PURPLE_MEDIA_VIDEO
)
389 return FS_DIRECTION_BOTH
;
390 else if ((type
& PURPLE_MEDIA_SEND_AUDIO
) ||
391 (type
& PURPLE_MEDIA_SEND_VIDEO
))
392 return FS_DIRECTION_SEND
;
393 else if ((type
& PURPLE_MEDIA_RECV_AUDIO
) ||
394 (type
& PURPLE_MEDIA_RECV_VIDEO
))
395 return FS_DIRECTION_RECV
;
397 return FS_DIRECTION_NONE
;
400 static PurpleMediaSessionType
401 session_type_from_fs(FsMediaType type
, FsStreamDirection direction
)
403 PurpleMediaSessionType result
= PURPLE_MEDIA_NONE
;
404 if (type
== FS_MEDIA_TYPE_AUDIO
) {
405 if (direction
& FS_DIRECTION_SEND
)
406 result
|= PURPLE_MEDIA_SEND_AUDIO
;
407 if (direction
& FS_DIRECTION_RECV
)
408 result
|= PURPLE_MEDIA_RECV_AUDIO
;
409 } else if (type
== FS_MEDIA_TYPE_VIDEO
) {
410 if (direction
& FS_DIRECTION_SEND
)
411 result
|= PURPLE_MEDIA_SEND_VIDEO
;
412 if (direction
& FS_DIRECTION_RECV
)
413 result
|= PURPLE_MEDIA_RECV_VIDEO
;
419 candidate_to_fs(PurpleMediaCandidate
*candidate
)
421 FsCandidate
*fscandidate
;
428 PurpleMediaNetworkProtocol proto
;
430 PurpleMediaCandidateType type
;
435 if (candidate
== NULL
)
438 g_object_get(G_OBJECT(candidate
),
439 "foundation", &foundation
,
440 "component-id", &component_id
,
444 "base-port", &base_port
,
446 "priority", &priority
,
448 "username", &username
,
449 "password", &password
,
453 fscandidate
= fs_candidate_new(foundation
,
457 fscandidate
->base_ip
= base_ip
;
458 fscandidate
->base_port
= base_port
;
459 fscandidate
->priority
= priority
;
460 fscandidate
->username
= username
;
461 fscandidate
->password
= password
;
462 fscandidate
->ttl
= ttl
;
470 candidate_list_to_fs(GList
*candidates
)
472 GList
*new_list
= NULL
;
474 for (; candidates
; candidates
= g_list_next(candidates
)) {
475 new_list
= g_list_prepend(new_list
,
476 candidate_to_fs(candidates
->data
));
479 new_list
= g_list_reverse(new_list
);
483 static PurpleMediaCandidate
*
484 candidate_from_fs(FsCandidate
*fscandidate
)
486 PurpleMediaCandidate
*candidate
;
488 if (fscandidate
== NULL
)
491 candidate
= purple_media_candidate_new(fscandidate
->foundation
,
492 fscandidate
->component_id
, fscandidate
->type
,
493 fscandidate
->proto
, fscandidate
->ip
, fscandidate
->port
);
494 g_object_set(candidate
,
495 "base-ip", fscandidate
->base_ip
,
496 "base-port", fscandidate
->base_port
,
497 "priority", fscandidate
->priority
,
498 "username", fscandidate
->username
,
499 "password", fscandidate
->password
,
500 "ttl", fscandidate
->ttl
, NULL
);
505 candidate_list_from_fs(GList
*candidates
)
507 GList
*new_list
= NULL
;
509 for (; candidates
; candidates
= g_list_next(candidates
)) {
510 new_list
= g_list_prepend(new_list
,
511 candidate_from_fs(candidates
->data
));
514 new_list
= g_list_reverse(new_list
);
519 codec_to_fs(const PurpleMediaCodec
*codec
)
524 PurpleMediaSessionType media_type
;
532 g_object_get(G_OBJECT(codec
),
534 "encoding-name", &encoding_name
,
535 "media-type", &media_type
,
536 "clock-rate", &clock_rate
,
537 "channels", &channels
,
538 "optional-params", &iter
,
541 new_codec
= fs_codec_new(id
, encoding_name
,
542 session_type_to_fs_media_type(media_type
),
544 new_codec
->channels
= channels
;
546 for (; iter
; iter
= g_list_next(iter
)) {
547 PurpleKeyValuePair
*param
= (PurpleKeyValuePair
*)iter
->data
;
548 fs_codec_add_optional_parameter(new_codec
,
549 param
->key
, param
->value
);
552 g_free(encoding_name
);
556 static PurpleMediaCodec
*
557 codec_from_fs(const FsCodec
*codec
)
559 PurpleMediaCodec
*new_codec
;
565 new_codec
= purple_media_codec_new(codec
->id
, codec
->encoding_name
,
566 session_type_from_fs(codec
->media_type
,
567 FS_DIRECTION_BOTH
), codec
->clock_rate
);
568 g_object_set(new_codec
, "channels", codec
->channels
, NULL
);
570 for (iter
= codec
->optional_params
; iter
; iter
= g_list_next(iter
)) {
571 FsCodecParameter
*param
= (FsCodecParameter
*)iter
->data
;
572 purple_media_codec_add_optional_parameter(new_codec
,
573 param
->name
, param
->value
);
580 codec_list_from_fs(GList
*codecs
)
582 GList
*new_list
= NULL
;
584 for (; codecs
; codecs
= g_list_next(codecs
)) {
585 new_list
= g_list_prepend(new_list
,
586 codec_from_fs(codecs
->data
));
589 new_list
= g_list_reverse(new_list
);
594 codec_list_to_fs(GList
*codecs
)
596 GList
*new_list
= NULL
;
598 for (; codecs
; codecs
= g_list_next(codecs
)) {
599 new_list
= g_list_prepend(new_list
,
600 codec_to_fs(codecs
->data
));
603 new_list
= g_list_reverse(new_list
);
607 static PurpleMediaBackendFs2Session
*
608 get_session(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
)
610 PurpleMediaBackendFs2Private
*priv
;
611 PurpleMediaBackendFs2Session
*session
= NULL
;
613 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
615 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
617 if (priv
->sessions
!= NULL
)
618 session
= g_hash_table_lookup(priv
->sessions
, sess_id
);
623 static FsParticipant
*
624 get_participant(PurpleMediaBackendFs2
*self
, const gchar
*name
)
626 PurpleMediaBackendFs2Private
*priv
;
627 FsParticipant
*participant
= NULL
;
629 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
631 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
633 if (priv
->participants
!= NULL
)
634 participant
= g_hash_table_lookup(priv
->participants
, name
);
639 static PurpleMediaBackendFs2Stream
*
640 get_stream(PurpleMediaBackendFs2
*self
,
641 const gchar
*sess_id
, const gchar
*name
)
643 PurpleMediaBackendFs2Private
*priv
;
646 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
648 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
649 streams
= priv
->streams
;
651 for (; streams
; streams
= g_list_next(streams
)) {
652 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
653 if (!strcmp(stream
->session
->id
, sess_id
) &&
654 !strcmp(stream
->participant
, name
))
662 get_streams(PurpleMediaBackendFs2
*self
,
663 const gchar
*sess_id
, const gchar
*name
)
665 PurpleMediaBackendFs2Private
*priv
;
666 GList
*streams
, *ret
= NULL
;
668 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
670 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
671 streams
= priv
->streams
;
673 for (; streams
; streams
= g_list_next(streams
)) {
674 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
676 if (sess_id
!= NULL
&& strcmp(stream
->session
->id
, sess_id
))
678 else if (name
!= NULL
&& strcmp(stream
->participant
, name
))
681 ret
= g_list_prepend(ret
, stream
);
684 ret
= g_list_reverse(ret
);
688 static PurpleMediaBackendFs2Session
*
689 get_session_from_fs_stream(PurpleMediaBackendFs2
*self
, FsStream
*stream
)
691 PurpleMediaBackendFs2Private
*priv
=
692 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
693 FsSession
*fssession
;
696 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
697 g_return_val_if_fail(FS_IS_STREAM(stream
), NULL
);
699 g_object_get(stream
, "session", &fssession
, NULL
);
701 values
= g_hash_table_get_values(priv
->sessions
);
703 for (; values
; values
= g_list_delete_link(values
, values
)) {
704 PurpleMediaBackendFs2Session
*session
= values
->data
;
706 if (session
->session
== fssession
) {
708 g_object_unref(fssession
);
713 g_object_unref(fssession
);
718 gst_handle_message_element(GstBus
*bus
, GstMessage
*msg
,
719 PurpleMediaBackendFs2
*self
)
721 PurpleMediaBackendFs2Private
*priv
=
722 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
723 GstElement
*src
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
724 static guint level_id
= 0;
727 level_id
= g_signal_lookup("level", PURPLE_TYPE_MEDIA
);
729 if (g_signal_has_handler_pending(priv
->media
, level_id
, 0, FALSE
)
730 && gst_structure_has_name(
731 gst_message_get_structure(msg
), "level")) {
732 GstElement
*src
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
734 gchar
*participant
= NULL
;
735 PurpleMediaBackendFs2Session
*session
= NULL
;
741 if (!PURPLE_IS_MEDIA(priv
->media
) ||
742 GST_ELEMENT_PARENT(src
) != priv
->confbin
)
745 name
= gst_element_get_name(src
);
747 if (!strncmp(name
, "sendlevel_", 10)) {
748 session
= get_session(self
, name
+10);
750 GList
*iter
= priv
->streams
;
751 PurpleMediaBackendFs2Stream
*stream
;
752 for (; iter
; iter
= g_list_next(iter
)) {
754 if (stream
->level
== src
) {
755 session
= stream
->session
;
756 participant
= stream
->participant
;
767 list
= gst_structure_get_value(
768 gst_message_get_structure(msg
), "rms");
769 value
= gst_value_list_get_value(list
, 0);
770 rms_db
= g_value_get_double(value
);
771 percent
= pow(10, rms_db
/ 20) * 5;
776 g_signal_emit(priv
->media
, level_id
, 0,
777 session
->id
, participant
, percent
);
781 if (!FS_IS_CONFERENCE(src
) || !PURPLE_IS_MEDIA_BACKEND(self
) ||
782 priv
->conference
!= FS_CONFERENCE(src
))
785 if (gst_structure_has_name(msg
->structure
, "farsight-error")) {
787 gst_structure_get_enum(msg
->structure
, "error-no",
788 FS_TYPE_ERROR
, (gint
*)&error_no
);
790 case FS_ERROR_NO_CODECS
:
791 purple_media_error(priv
->media
, _("No codecs"
792 " found. Install some"
793 " GStreamer codecs found"
794 " in GStreamer plugins"
796 purple_media_end(priv
->media
, NULL
, NULL
);
798 case FS_ERROR_NO_CODECS_LEFT
:
799 purple_media_error(priv
->media
, _("No codecs"
802 " fs-codecs.conf are too"
804 purple_media_end(priv
->media
, NULL
, NULL
);
806 case FS_ERROR_UNKNOWN_CNAME
:
808 * Unknown CName is only a problem for the
809 * multicast transmitter which isn't used.
810 * It is also deprecated.
814 purple_debug_error("backend-fs2",
815 "farsight-error: %i: %s\n",
817 gst_structure_get_string(
818 msg
->structure
, "error-msg"));
822 if (FS_ERROR_IS_FATAL(error_no
)) {
823 purple_media_error(priv
->media
, _("A non-recoverable "
824 "Farsight2 error has occurred."));
825 purple_media_end(priv
->media
, NULL
, NULL
);
827 } else if (gst_structure_has_name(msg
->structure
,
828 "farsight-new-local-candidate")) {
831 FsCandidate
*local_candidate
;
832 PurpleMediaCandidate
*candidate
;
833 FsParticipant
*participant
;
834 PurpleMediaBackendFs2Session
*session
;
835 PurpleMediaBackendFs2Stream
*media_stream
;
838 value
= gst_structure_get_value(msg
->structure
, "stream");
839 stream
= g_value_get_object(value
);
840 value
= gst_structure_get_value(msg
->structure
, "candidate");
841 local_candidate
= g_value_get_boxed(value
);
843 session
= get_session_from_fs_stream(self
, stream
);
845 purple_debug_info("backend-fs2",
846 "got new local candidate: %s\n",
847 local_candidate
->foundation
);
849 g_object_get(stream
, "participant", &participant
, NULL
);
850 g_object_get(participant
, "cname", &name
, NULL
);
851 g_object_unref(participant
);
853 media_stream
= get_stream(self
, session
->id
, name
);
854 media_stream
->local_candidates
= g_list_append(
855 media_stream
->local_candidates
,
856 fs_candidate_copy(local_candidate
));
858 candidate
= candidate_from_fs(local_candidate
);
859 g_signal_emit_by_name(self
, "new-candidate",
860 session
->id
, name
, candidate
);
861 g_object_unref(candidate
);
862 } else if (gst_structure_has_name(msg
->structure
,
863 "farsight-local-candidates-prepared")) {
866 FsParticipant
*participant
;
867 PurpleMediaBackendFs2Session
*session
;
870 value
= gst_structure_get_value(msg
->structure
, "stream");
871 stream
= g_value_get_object(value
);
872 session
= get_session_from_fs_stream(self
, stream
);
874 g_object_get(stream
, "participant", &participant
, NULL
);
875 g_object_get(participant
, "cname", &name
, NULL
);
876 g_object_unref(participant
);
878 g_signal_emit_by_name(self
, "candidates-prepared",
880 } else if (gst_structure_has_name(msg
->structure
,
881 "farsight-new-active-candidate-pair")) {
884 FsCandidate
*local_candidate
;
885 FsCandidate
*remote_candidate
;
886 FsParticipant
*participant
;
887 PurpleMediaBackendFs2Session
*session
;
888 PurpleMediaCandidate
*lcandidate
, *rcandidate
;
891 value
= gst_structure_get_value(msg
->structure
, "stream");
892 stream
= g_value_get_object(value
);
893 value
= gst_structure_get_value(msg
->structure
,
895 local_candidate
= g_value_get_boxed(value
);
896 value
= gst_structure_get_value(msg
->structure
,
898 remote_candidate
= g_value_get_boxed(value
);
900 g_object_get(stream
, "participant", &participant
, NULL
);
901 g_object_get(participant
, "cname", &name
, NULL
);
902 g_object_unref(participant
);
904 session
= get_session_from_fs_stream(self
, stream
);
906 lcandidate
= candidate_from_fs(local_candidate
);
907 rcandidate
= candidate_from_fs(remote_candidate
);
909 g_signal_emit_by_name(self
, "active-candidate-pair",
910 session
->id
, name
, lcandidate
, rcandidate
);
912 g_object_unref(lcandidate
);
913 g_object_unref(rcandidate
);
914 } else if (gst_structure_has_name(msg
->structure
,
915 "farsight-recv-codecs-changed")) {
920 value
= gst_structure_get_value(msg
->structure
, "codecs");
921 codecs
= g_value_get_boxed(value
);
922 codec
= codecs
->data
;
924 purple_debug_info("backend-fs2",
925 "farsight-recv-codecs-changed: %s\n",
926 codec
->encoding_name
);
927 } else if (gst_structure_has_name(msg
->structure
,
928 "farsight-component-state-changed")) {
930 FsStreamState fsstate
;
934 value
= gst_structure_get_value(msg
->structure
, "state");
935 fsstate
= g_value_get_enum(value
);
936 value
= gst_structure_get_value(msg
->structure
, "component");
937 component
= g_value_get_uint(value
);
940 case FS_STREAM_STATE_FAILED
:
943 case FS_STREAM_STATE_DISCONNECTED
:
944 state
= "DISCONNECTED";
946 case FS_STREAM_STATE_GATHERING
:
949 case FS_STREAM_STATE_CONNECTING
:
950 state
= "CONNECTING";
952 case FS_STREAM_STATE_CONNECTED
:
955 case FS_STREAM_STATE_READY
:
963 purple_debug_info("backend-fs2",
964 "farsight-component-state-changed: "
965 "component: %u state: %s\n",
967 } else if (gst_structure_has_name(msg
->structure
,
968 "farsight-send-codec-changed")) {
973 value
= gst_structure_get_value(msg
->structure
, "codec");
974 codec
= g_value_get_boxed(value
);
975 codec_str
= fs_codec_to_string(codec
);
977 purple_debug_info("backend-fs2",
978 "farsight-send-codec-changed: codec: %s\n",
982 } else if (gst_structure_has_name(msg
->structure
,
983 "farsight-codecs-changed")) {
985 FsSession
*fssession
;
988 value
= gst_structure_get_value(msg
->structure
, "session");
989 fssession
= g_value_get_object(value
);
990 sessions
= g_hash_table_get_values(priv
->sessions
);
992 for (; sessions
; sessions
=
993 g_list_delete_link(sessions
, sessions
)) {
994 PurpleMediaBackendFs2Session
*session
= sessions
->data
;
997 if (session
->session
!= fssession
)
1000 session_id
= g_strdup(session
->id
);
1001 g_signal_emit_by_name(self
, "codecs-changed",
1004 g_list_free(sessions
);
1011 gst_handle_message_error(GstBus
*bus
, GstMessage
*msg
,
1012 PurpleMediaBackendFs2
*self
)
1014 PurpleMediaBackendFs2Private
*priv
=
1015 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
1016 GstElement
*element
= GST_ELEMENT(GST_MESSAGE_SRC(msg
));
1017 GstElement
*lastElement
= NULL
;
1020 while (!GST_IS_PIPELINE(element
)) {
1021 if (element
== priv
->confbin
)
1024 lastElement
= element
;
1025 element
= GST_ELEMENT_PARENT(element
);
1028 if (!GST_IS_PIPELINE(element
))
1031 sessions
= purple_media_get_session_ids(priv
->media
);
1033 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
1034 if (purple_media_get_src(priv
->media
, sessions
->data
)
1038 if (purple_media_get_session_type(priv
->media
, sessions
->data
)
1039 & PURPLE_MEDIA_AUDIO
)
1040 purple_media_error(priv
->media
,
1041 _("Error with your microphone"));
1043 purple_media_error(priv
->media
,
1044 _("Error with your webcam"));
1049 g_list_free(sessions
);
1051 purple_media_error(priv
->media
, _("Conference error"));
1052 purple_media_end(priv
->media
, NULL
, NULL
);
1056 gst_bus_cb(GstBus
*bus
, GstMessage
*msg
, PurpleMediaBackendFs2
*self
)
1058 switch(GST_MESSAGE_TYPE(msg
)) {
1059 case GST_MESSAGE_ELEMENT
:
1060 gst_handle_message_element(bus
, msg
, self
);
1062 case GST_MESSAGE_ERROR
:
1063 gst_handle_message_error(bus
, msg
, self
);
1073 state_changed_cb(PurpleMedia
*media
, PurpleMediaState state
,
1074 gchar
*sid
, gchar
*name
, PurpleMediaBackendFs2
*self
)
1079 stream_info_cb(PurpleMedia
*media
, PurpleMediaInfoType type
,
1080 gchar
*sid
, gchar
*name
, gboolean local
,
1081 PurpleMediaBackendFs2
*self
)
1083 if (type
== PURPLE_MEDIA_INFO_ACCEPT
&& sid
!= NULL
&& name
!= NULL
) {
1084 PurpleMediaBackendFs2Stream
*stream
=
1085 get_stream(self
, sid
, name
);
1088 g_object_set(G_OBJECT(stream
->stream
), "direction",
1089 session_type_to_fs_stream_direction(
1090 stream
->session
->type
), NULL
);
1092 if (stream
->remote_candidates
== NULL
||
1093 purple_media_is_initiator(media
, sid
, name
))
1096 fs_stream_set_remote_candidates(stream
->stream
,
1097 stream
->remote_candidates
, &err
);
1102 purple_debug_error("backend-fs2", "Error adding "
1103 "remote candidates: %s\n",
1106 } else if (local
== TRUE
&& (type
== PURPLE_MEDIA_INFO_MUTE
||
1107 type
== PURPLE_MEDIA_INFO_UNMUTE
)) {
1108 PurpleMediaBackendFs2Private
*priv
=
1109 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
1110 gboolean active
= (type
== PURPLE_MEDIA_INFO_MUTE
);
1114 sessions
= g_hash_table_get_values(priv
->sessions
);
1116 sessions
= g_list_prepend(NULL
,
1117 get_session(self
, sid
));
1119 purple_debug_info("media", "Turning mute %s\n",
1120 active
? "on" : "off");
1122 for (; sessions
; sessions
= g_list_delete_link(
1123 sessions
, sessions
)) {
1124 PurpleMediaBackendFs2Session
*session
=
1127 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
1128 gchar
*name
= g_strdup_printf("volume_%s",
1130 GstElement
*volume
= gst_bin_get_by_name(
1131 GST_BIN(priv
->confbin
), name
);
1133 g_object_set(volume
, "mute", active
, NULL
);
1136 } else if (local
== TRUE
&& (type
== PURPLE_MEDIA_INFO_PAUSE
||
1137 type
== PURPLE_MEDIA_INFO_UNPAUSE
)) {
1138 gboolean active
= (type
== PURPLE_MEDIA_INFO_PAUSE
);
1139 GList
*streams
= get_streams(self
, sid
, name
);
1140 for (; streams
; streams
=
1141 g_list_delete_link(streams
, streams
)) {
1142 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
1143 if (stream
->session
->type
& PURPLE_MEDIA_SEND_VIDEO
) {
1144 g_object_set(stream
->stream
, "direction",
1145 session_type_to_fs_stream_direction(
1146 stream
->session
->type
& ((active
) ?
1147 ~PURPLE_MEDIA_SEND_VIDEO
:
1148 PURPLE_MEDIA_VIDEO
)), NULL
);
1155 init_conference(PurpleMediaBackendFs2
*self
)
1157 PurpleMediaBackendFs2Private
*priv
=
1158 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
1159 GstElement
*pipeline
;
1163 priv
->conference
= FS_CONFERENCE(
1164 gst_element_factory_make(priv
->conference_type
, NULL
));
1166 if (priv
->conference
== NULL
) {
1167 purple_debug_error("backend-fs2", "Conference == NULL\n");
1171 pipeline
= purple_media_manager_get_pipeline(
1172 purple_media_get_manager(priv
->media
));
1174 if (pipeline
== NULL
) {
1175 purple_debug_error("backend-fs2",
1176 "Couldn't retrieve pipeline.\n");
1180 name
= g_strdup_printf("conf_%p", priv
->conference
);
1181 priv
->confbin
= gst_bin_new(name
);
1182 if (priv
->confbin
== NULL
) {
1183 purple_debug_error("backend-fs2",
1184 "Couldn't create confbin.\n");
1190 bus
= gst_pipeline_get_bus(GST_PIPELINE(pipeline
));
1192 purple_debug_error("backend-fs2",
1193 "Couldn't get the pipeline's bus.\n");
1197 g_signal_connect(G_OBJECT(bus
), "message",
1198 G_CALLBACK(gst_bus_cb
), self
);
1199 gst_object_unref(bus
);
1201 if (!gst_bin_add(GST_BIN(pipeline
),
1202 GST_ELEMENT(priv
->confbin
))) {
1203 purple_debug_error("backend-fs2", "Couldn't add confbin "
1204 "element to the pipeline\n");
1208 if (!gst_bin_add(GST_BIN(priv
->confbin
),
1209 GST_ELEMENT(priv
->conference
))) {
1210 purple_debug_error("backend-fs2", "Couldn't add conference "
1211 "element to the confbin\n");
1215 if (gst_element_set_state(GST_ELEMENT(priv
->confbin
),
1216 GST_STATE_PLAYING
) == GST_STATE_CHANGE_FAILURE
) {
1217 purple_debug_error("backend-fs2",
1218 "Failed to start conference.\n");
1226 gst_element_added_cb(FsElementAddedNotifier
*self
,
1227 GstBin
*bin
, GstElement
*element
, gpointer user_data
)
1230 * Hack to make H264 work with Gmail video.
1232 if (!strncmp(GST_ELEMENT_NAME(element
), "x264", 4)) {
1233 g_object_set(GST_OBJECT(element
), "cabac", FALSE
, NULL
);
1238 create_src(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
,
1239 PurpleMediaSessionType type
)
1241 PurpleMediaBackendFs2Private
*priv
=
1242 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
1243 PurpleMediaBackendFs2Session
*session
;
1244 PurpleMediaSessionType session_type
;
1245 FsMediaType media_type
= session_type_to_fs_media_type(type
);
1246 FsStreamDirection type_direction
=
1247 session_type_to_fs_stream_direction(type
);
1249 GstPad
*sinkpad
, *srcpad
;
1251 if ((type_direction
& FS_DIRECTION_SEND
) == 0)
1254 session_type
= session_type_from_fs(
1255 media_type
, FS_DIRECTION_SEND
);
1256 src
= purple_media_manager_get_element(
1257 purple_media_get_manager(priv
->media
),
1258 session_type
, priv
->media
, sess_id
, NULL
);
1260 if (!GST_IS_ELEMENT(src
)) {
1261 purple_debug_error("backend-fs2",
1262 "Error creating src for session %s\n",
1267 session
= get_session(self
, sess_id
);
1269 if (session
== NULL
) {
1270 purple_debug_warning("backend-fs2",
1271 "purple_media_set_src: trying to set"
1272 " src on non-existent session\n");
1277 gst_object_unref(session
->src
);
1280 gst_element_set_locked_state(session
->src
, TRUE
);
1282 session
->tee
= gst_element_factory_make("tee", NULL
);
1283 gst_bin_add(GST_BIN(priv
->confbin
), session
->tee
);
1285 /* This supposedly isn't necessary, but it silences some warnings */
1286 if (GST_ELEMENT_PARENT(priv
->confbin
)
1287 == GST_ELEMENT_PARENT(session
->src
)) {
1288 GstPad
*pad
= gst_element_get_static_pad(session
->tee
, "sink");
1289 GstPad
*ghost
= gst_ghost_pad_new(NULL
, pad
);
1290 gst_object_unref(pad
);
1291 gst_pad_set_active(ghost
, TRUE
);
1292 gst_element_add_pad(priv
->confbin
, ghost
);
1295 gst_element_set_state(session
->tee
, GST_STATE_PLAYING
);
1296 gst_element_link(session
->src
, priv
->confbin
);
1298 g_object_get(session
->session
, "sink-pad", &sinkpad
, NULL
);
1299 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
1300 gchar
*name
= g_strdup_printf("volume_%s", session
->id
);
1302 GstElement
*volume
= gst_element_factory_make("volume", name
);
1303 double input_volume
= purple_prefs_get_int(
1304 "/purple/media/audio/volume/input")/10.0;
1306 name
= g_strdup_printf("sendlevel_%s", session
->id
);
1307 level
= gst_element_factory_make("level", name
);
1309 gst_bin_add(GST_BIN(priv
->confbin
), volume
);
1310 gst_bin_add(GST_BIN(priv
->confbin
), level
);
1311 gst_element_set_state(level
, GST_STATE_PLAYING
);
1312 gst_element_set_state(volume
, GST_STATE_PLAYING
);
1313 gst_element_link(volume
, level
);
1314 gst_element_link(session
->tee
, volume
);
1315 srcpad
= gst_element_get_static_pad(level
, "src");
1316 g_object_set(volume
, "volume", input_volume
, NULL
);
1318 srcpad
= gst_element_get_request_pad(session
->tee
, "src%d");
1321 purple_debug_info("backend-fs2", "connecting pad: %s\n",
1322 gst_pad_link(srcpad
, sinkpad
) == GST_PAD_LINK_OK
1323 ? "success" : "failure");
1324 gst_element_set_locked_state(session
->src
, FALSE
);
1325 gst_object_unref(session
->src
);
1327 gst_element_set_state(session
->src
, GST_STATE_PLAYING
);
1329 purple_media_manager_create_output_window(purple_media_get_manager(
1330 priv
->media
), priv
->media
, sess_id
, NULL
);
1336 create_session(PurpleMediaBackendFs2
*self
, const gchar
*sess_id
,
1337 PurpleMediaSessionType type
, gboolean initiator
,
1338 const gchar
*transmitter
)
1340 PurpleMediaBackendFs2Private
*priv
=
1341 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
1342 PurpleMediaBackendFs2Session
*session
;
1344 GList
*codec_conf
= NULL
, *iter
= NULL
;
1345 gchar
*filename
= NULL
;
1346 gboolean is_nice
= !strcmp(transmitter
, "nice");
1348 session
= g_new0(PurpleMediaBackendFs2Session
, 1);
1350 session
->session
= fs_conference_new_session(priv
->conference
,
1351 session_type_to_fs_media_type(type
), &err
);
1354 purple_media_error(priv
->media
,
1355 _("Error creating session: %s"),
1362 filename
= g_build_filename(purple_user_dir(), "fs-codec.conf", NULL
);
1363 codec_conf
= fs_codec_list_from_keyfile(filename
, &err
);
1368 purple_debug_info("backend-fs2", "Couldn't read "
1369 "fs-codec.conf: %s\n",
1372 purple_debug_error("backend-fs2", "Error reading "
1373 "fs-codec.conf: %s\n",
1379 * Add SPEEX if the configuration file doesn't exist or
1380 * there isn't a speex entry.
1382 for (iter
= codec_conf
; iter
; iter
= g_list_next(iter
)) {
1383 FsCodec
*codec
= iter
->data
;
1384 if (!g_ascii_strcasecmp(codec
->encoding_name
, "speex"))
1389 codec_conf
= g_list_prepend(codec_conf
,
1390 fs_codec_new(FS_CODEC_ID_ANY
,
1391 "SPEEX", FS_MEDIA_TYPE_AUDIO
, 8000));
1392 codec_conf
= g_list_prepend(codec_conf
,
1393 fs_codec_new(FS_CODEC_ID_ANY
,
1394 "SPEEX", FS_MEDIA_TYPE_AUDIO
, 16000));
1397 fs_session_set_codec_preferences(session
->session
, codec_conf
, NULL
);
1398 fs_codec_list_destroy(codec_conf
);
1401 * Removes a 5-7 second delay before
1402 * receiving the src-pad-added signal.
1403 * Only works for non-multicast FsRtpSessions.
1405 if (is_nice
|| !strcmp(transmitter
, "rawudp"))
1406 g_object_set(G_OBJECT(session
->session
),
1407 "no-rtcp-timeout", 0, NULL
);
1410 * Hack to make x264 work with Gmail video.
1412 if (is_nice
&& !strcmp(sess_id
, "google-video")) {
1413 FsElementAddedNotifier
*notifier
=
1414 fs_element_added_notifier_new();
1415 g_signal_connect(G_OBJECT(notifier
), "element-added",
1416 G_CALLBACK(gst_element_added_cb
), NULL
);
1417 fs_element_added_notifier_add(notifier
,
1418 GST_BIN(priv
->conference
));
1421 session
->id
= g_strdup(sess_id
);
1422 session
->backend
= self
;
1423 session
->type
= type
;
1425 if (!priv
->sessions
) {
1426 purple_debug_info("backend-fs2",
1427 "Creating hash table for sessions\n");
1428 priv
->sessions
= g_hash_table_new(g_str_hash
, g_str_equal
);
1431 g_hash_table_insert(priv
->sessions
, g_strdup(session
->id
), session
);
1433 if (!create_src(self
, sess_id
, type
)) {
1434 purple_debug_info("backend-fs2", "Error creating the src\n");
1442 create_participant(PurpleMediaBackendFs2
*self
, const gchar
*name
)
1444 PurpleMediaBackendFs2Private
*priv
=
1445 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
1446 FsParticipant
*participant
;
1449 participant
= fs_conference_new_participant(
1450 priv
->conference
, name
, &err
);
1453 purple_debug_error("backend-fs2",
1454 "Error creating participant: %s\n",
1460 if (!priv
->participants
) {
1461 purple_debug_info("backend-fs2",
1462 "Creating hash table for participants\n");
1463 priv
->participants
= g_hash_table_new_full(g_str_hash
,
1464 g_str_equal
, g_free
, NULL
);
1467 g_hash_table_insert(priv
->participants
, g_strdup(name
), participant
);
1473 src_pad_added_cb_cb(PurpleMediaBackendFs2Stream
*stream
)
1475 PurpleMediaBackendFs2Private
*priv
;
1477 g_return_val_if_fail(stream
!= NULL
, FALSE
);
1479 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(stream
->session
->backend
);
1480 stream
->connected_cb_id
= 0;
1482 purple_media_manager_create_output_window(
1483 purple_media_get_manager(priv
->media
), priv
->media
,
1484 stream
->session
->id
, stream
->participant
);
1486 g_signal_emit_by_name(priv
->media
, "state-changed",
1487 PURPLE_MEDIA_STATE_CONNECTED
,
1488 stream
->session
->id
, stream
->participant
);
1493 src_pad_added_cb(FsStream
*fsstream
, GstPad
*srcpad
,
1494 FsCodec
*codec
, PurpleMediaBackendFs2Stream
*stream
)
1496 PurpleMediaBackendFs2Private
*priv
;
1499 g_return_if_fail(FS_IS_STREAM(fsstream
));
1500 g_return_if_fail(stream
!= NULL
);
1502 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(stream
->session
->backend
);
1504 if (stream
->src
== NULL
) {
1505 GstElement
*sink
= NULL
;
1507 if (codec
->media_type
== FS_MEDIA_TYPE_AUDIO
) {
1508 GstElement
*queue
= NULL
;
1509 double output_volume
= purple_prefs_get_int(
1510 "/purple/media/audio/volume/output")/10.0;
1512 * Should this instead be:
1513 * audioconvert ! audioresample ! liveadder !
1514 * audioresample ! audioconvert ! realsink
1516 queue
= gst_element_factory_make("queue", NULL
);
1517 stream
->volume
= gst_element_factory_make(
1519 g_object_set(stream
->volume
, "volume",
1520 output_volume
, NULL
);
1521 stream
->level
= gst_element_factory_make(
1523 stream
->src
= gst_element_factory_make(
1525 sink
= purple_media_manager_get_element(
1526 purple_media_get_manager(priv
->media
),
1527 PURPLE_MEDIA_RECV_AUDIO
, priv
->media
,
1528 stream
->session
->id
,
1529 stream
->participant
);
1530 gst_bin_add(GST_BIN(priv
->confbin
), queue
);
1531 gst_bin_add(GST_BIN(priv
->confbin
), stream
->volume
);
1532 gst_bin_add(GST_BIN(priv
->confbin
), stream
->level
);
1533 gst_bin_add(GST_BIN(priv
->confbin
), sink
);
1534 gst_element_set_state(sink
, GST_STATE_PLAYING
);
1535 gst_element_set_state(stream
->level
, GST_STATE_PLAYING
);
1536 gst_element_set_state(stream
->volume
, GST_STATE_PLAYING
);
1537 gst_element_set_state(queue
, GST_STATE_PLAYING
);
1538 gst_element_link(stream
->level
, sink
);
1539 gst_element_link(stream
->volume
, stream
->level
);
1540 gst_element_link(queue
, stream
->volume
);
1542 } else if (codec
->media_type
== FS_MEDIA_TYPE_VIDEO
) {
1543 stream
->src
= gst_element_factory_make(
1545 sink
= gst_element_factory_make(
1547 g_object_set(G_OBJECT(sink
), "async", FALSE
, NULL
);
1548 gst_bin_add(GST_BIN(priv
->confbin
), sink
);
1549 gst_element_set_state(sink
, GST_STATE_PLAYING
);
1551 stream
->tee
= gst_element_factory_make("tee", NULL
);
1552 gst_bin_add_many(GST_BIN(priv
->confbin
),
1553 stream
->src
, stream
->tee
, NULL
);
1554 gst_element_set_state(stream
->tee
, GST_STATE_PLAYING
);
1555 gst_element_set_state(stream
->src
, GST_STATE_PLAYING
);
1556 gst_element_link_many(stream
->src
, stream
->tee
, sink
, NULL
);
1559 sinkpad
= gst_element_get_request_pad(stream
->src
, "sink%d");
1560 gst_pad_link(srcpad
, sinkpad
);
1561 gst_object_unref(sinkpad
);
1563 stream
->connected_cb_id
= purple_timeout_add(0,
1564 (GSourceFunc
)src_pad_added_cb_cb
, stream
);
1568 create_stream(PurpleMediaBackendFs2
*self
,
1569 const gchar
*sess_id
, const gchar
*who
,
1570 PurpleMediaSessionType type
, gboolean initiator
,
1571 const gchar
*transmitter
,
1572 guint num_params
, GParameter
*params
)
1574 PurpleMediaBackendFs2Private
*priv
=
1575 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
1577 FsStream
*fsstream
= NULL
;
1578 const gchar
*stun_ip
= purple_network_get_stun_ip();
1579 const gchar
*turn_ip
= purple_network_get_turn_ip();
1580 guint _num_params
= num_params
;
1581 GParameter
*_params
= g_new0(GParameter
, num_params
+ 3);
1582 FsStreamDirection type_direction
=
1583 session_type_to_fs_stream_direction(type
);
1584 PurpleMediaBackendFs2Session
*session
;
1585 PurpleMediaBackendFs2Stream
*stream
;
1586 FsParticipant
*participant
;
1588 memcpy(_params
, params
, sizeof(GParameter
) * num_params
);
1590 /* set the controlling mode parameter */
1591 _params
[_num_params
].name
= "controlling-mode";
1592 g_value_init(&_params
[_num_params
].value
, G_TYPE_BOOLEAN
);
1593 g_value_set_boolean(&_params
[_num_params
].value
, initiator
);
1597 purple_debug_info("backend-fs2",
1598 "Setting stun-ip on new stream: %s\n", stun_ip
);
1600 _params
[_num_params
].name
= "stun-ip";
1601 g_value_init(&_params
[_num_params
].value
, G_TYPE_STRING
);
1602 g_value_set_string(&_params
[_num_params
].value
, stun_ip
);
1606 if (turn_ip
&& !strcmp("nice", transmitter
)) {
1607 GValueArray
*relay_info
= g_value_array_new(0);
1609 gint turn_port
= purple_prefs_get_int(
1610 "/purple/network/turn_port");
1611 const gchar
*username
= purple_prefs_get_string(
1612 "/purple/network/turn_username");
1613 const gchar
*password
= purple_prefs_get_string(
1614 "/purple/network/turn_password");
1615 GstStructure
*turn_setup
= gst_structure_new("relay-info",
1616 "ip", G_TYPE_STRING
, turn_ip
,
1617 "port", G_TYPE_UINT
, turn_port
,
1618 "username", G_TYPE_STRING
, username
,
1619 "password", G_TYPE_STRING
, password
,
1623 purple_debug_error("backend-fs2",
1624 "Error creating relay info structure");
1628 memset(&value
, 0, sizeof(GValue
));
1629 g_value_init(&value
, GST_TYPE_STRUCTURE
);
1630 gst_value_set_structure(&value
, turn_setup
);
1631 relay_info
= g_value_array_append(relay_info
, &value
);
1632 gst_structure_free(turn_setup
);
1634 purple_debug_info("backend-fs2",
1635 "Setting relay-info on new stream\n");
1636 _params
[_num_params
].name
= "relay-info";
1637 g_value_init(&_params
[_num_params
].value
,
1638 G_TYPE_VALUE_ARRAY
);
1639 g_value_set_boxed(&_params
[_num_params
].value
,
1641 g_value_array_free(relay_info
);
1645 session
= get_session(self
, sess_id
);
1647 if (session
== NULL
) {
1648 purple_debug_error("backend-fs2",
1649 "Couldn't find session to create stream.\n");
1653 participant
= get_participant(self
, who
);
1655 if (participant
== NULL
) {
1656 purple_debug_error("backend-fs2", "Couldn't find "
1657 "participant to create stream.\n");
1661 fsstream
= fs_session_new_stream(session
->session
, participant
,
1662 initiator
== TRUE
? type_direction
:
1663 (type_direction
& FS_DIRECTION_RECV
), transmitter
,
1664 _num_params
, _params
, &err
);
1667 if (fsstream
== NULL
) {
1669 purple_debug_error("backend-fs2",
1670 "Error creating stream: %s\n",
1671 err
&& err
->message
?
1672 err
->message
: "NULL");
1675 purple_debug_error("backend-fs2",
1676 "Error creating stream\n");
1680 stream
= g_new0(PurpleMediaBackendFs2Stream
, 1);
1681 stream
->participant
= g_strdup(who
);
1682 stream
->session
= session
;
1683 stream
->stream
= fsstream
;
1685 priv
->streams
= g_list_append(priv
->streams
, stream
);
1687 g_signal_connect(G_OBJECT(fsstream
), "src-pad-added",
1688 G_CALLBACK(src_pad_added_cb
), stream
);
1694 purple_media_backend_fs2_add_stream(PurpleMediaBackend
*self
,
1695 const gchar
*sess_id
, const gchar
*who
,
1696 PurpleMediaSessionType type
, gboolean initiator
,
1697 const gchar
*transmitter
,
1698 guint num_params
, GParameter
*params
)
1700 PurpleMediaBackendFs2
*backend
= PURPLE_MEDIA_BACKEND_FS2(self
);
1701 PurpleMediaBackendFs2Private
*priv
=
1702 PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(backend
);
1703 PurpleMediaBackendFs2Stream
*stream
;
1705 if (priv
->conference
== NULL
&& !init_conference(backend
)) {
1706 purple_debug_error("backend-fs2",
1707 "Error initializing the conference.\n");
1711 if (get_session(backend
, sess_id
) == NULL
&&
1712 !create_session(backend
, sess_id
, type
,
1713 initiator
, transmitter
)) {
1714 purple_debug_error("backend-fs2",
1715 "Error creating the session.\n");
1719 if (get_participant(backend
, who
) == NULL
&&
1720 !create_participant(backend
, who
)) {
1721 purple_debug_error("backend-fs2",
1722 "Error creating the participant.\n");
1726 stream
= get_stream(backend
, sess_id
, who
);
1728 if (stream
!= NULL
) {
1729 FsStreamDirection type_direction
=
1730 session_type_to_fs_stream_direction(type
);
1732 if (session_type_to_fs_stream_direction(
1733 stream
->session
->type
) != type_direction
) {
1734 /* change direction */
1735 g_object_set(stream
->stream
, "direction",
1736 type_direction
, NULL
);
1738 } else if (!create_stream(backend
, sess_id
, who
, type
,
1739 initiator
, transmitter
, num_params
, params
)) {
1740 purple_debug_error("backend-fs2",
1741 "Error creating the stream.\n");
1749 purple_media_backend_fs2_add_remote_candidates(PurpleMediaBackend
*self
,
1750 const gchar
*sess_id
, const gchar
*participant
,
1751 GList
*remote_candidates
)
1753 PurpleMediaBackendFs2Private
*priv
;
1754 PurpleMediaBackendFs2Stream
*stream
;
1757 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
1759 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
1760 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
1761 sess_id
, participant
);
1763 if (stream
== NULL
) {
1764 purple_debug_error("backend-fs2",
1765 "purple_media_add_remote_candidates: "
1766 "couldn't find stream %s %s.\n",
1767 sess_id
? sess_id
: "(null)",
1768 participant
? participant
: "(null)");
1772 stream
->remote_candidates
= g_list_concat(stream
->remote_candidates
,
1773 candidate_list_to_fs(remote_candidates
));
1775 if (purple_media_is_initiator(priv
->media
, sess_id
, participant
) ||
1776 purple_media_accepted(
1777 priv
->media
, sess_id
, participant
)) {
1778 fs_stream_set_remote_candidates(stream
->stream
,
1779 stream
->remote_candidates
, &err
);
1782 purple_debug_error("backend-fs2", "Error adding remote"
1783 " candidates: %s\n", err
->message
);
1790 purple_media_backend_fs2_codecs_ready(PurpleMediaBackend
*self
,
1791 const gchar
*sess_id
)
1793 PurpleMediaBackendFs2Private
*priv
;
1796 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
1798 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
1800 if (sess_id
!= NULL
) {
1801 PurpleMediaBackendFs2Session
*session
= get_session(
1802 PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
1804 if (session
== NULL
)
1807 if (session
->type
& (PURPLE_MEDIA_SEND_AUDIO
|
1808 PURPLE_MEDIA_SEND_VIDEO
))
1809 g_object_get(session
->session
,
1810 "codecs-ready", &ret
, NULL
);
1814 GList
*values
= g_hash_table_get_values(priv
->sessions
);
1816 for (; values
; values
= g_list_delete_link(values
, values
)) {
1817 PurpleMediaBackendFs2Session
*session
= values
->data
;
1818 if (session
->type
& (PURPLE_MEDIA_SEND_AUDIO
|
1819 PURPLE_MEDIA_SEND_VIDEO
))
1820 g_object_get(session
->session
,
1821 "codecs-ready", &ret
, NULL
);
1830 g_list_free(values
);
1837 purple_media_backend_fs2_get_codecs(PurpleMediaBackend
*self
,
1838 const gchar
*sess_id
)
1840 PurpleMediaBackendFs2Private
*priv
;
1841 PurpleMediaBackendFs2Session
*session
;
1845 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
1847 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
1849 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
1851 if (session
== NULL
)
1854 g_object_get(G_OBJECT(session
->session
),
1855 "codecs", &fscodecs
, NULL
);
1856 codecs
= codec_list_from_fs(fscodecs
);
1857 fs_codec_list_destroy(fscodecs
);
1863 purple_media_backend_fs2_get_local_candidates(PurpleMediaBackend
*self
,
1864 const gchar
*sess_id
, const gchar
*participant
)
1866 PurpleMediaBackendFs2Stream
*stream
;
1867 GList
*candidates
= NULL
;
1869 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), NULL
);
1871 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
1872 sess_id
, participant
);
1875 candidates
= candidate_list_from_fs(
1876 stream
->local_candidates
);
1881 purple_media_backend_fs2_set_remote_codecs(PurpleMediaBackend
*self
,
1882 const gchar
*sess_id
, const gchar
*participant
,
1885 PurpleMediaBackendFs2Stream
*stream
;
1889 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
1890 stream
= get_stream(PURPLE_MEDIA_BACKEND_FS2(self
),
1891 sess_id
, participant
);
1896 fscodecs
= codec_list_to_fs(codecs
);
1897 fs_stream_set_remote_codecs(stream
->stream
, fscodecs
, &err
);
1898 fs_codec_list_destroy(fscodecs
);
1901 purple_debug_error("backend-fs2",
1902 "Error setting remote codecs: %s\n",
1912 purple_media_backend_fs2_set_send_codec(PurpleMediaBackend
*self
,
1913 const gchar
*sess_id
, PurpleMediaCodec
*codec
)
1915 PurpleMediaBackendFs2Session
*session
;
1919 g_return_val_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
), FALSE
);
1921 session
= get_session(PURPLE_MEDIA_BACKEND_FS2(self
), sess_id
);
1923 if (session
== NULL
)
1926 fscodec
= codec_to_fs(codec
);
1927 fs_session_set_send_codec(session
->session
, fscodec
, &err
);
1928 fs_codec_destroy(fscodec
);
1931 purple_debug_error("media", "Error setting send codec\n");
1940 purple_media_backend_fs2_get_type(void)
1946 #ifdef USE_GSTREAMER
1948 purple_media_backend_fs2_get_src(PurpleMediaBackendFs2
*self
,
1949 const gchar
*sess_id
)
1952 PurpleMediaBackendFs2Session
*session
= get_session(self
, sess_id
);
1953 return session
!= NULL
? session
->src
: NULL
;
1960 purple_media_backend_fs2_get_tee(PurpleMediaBackendFs2
*self
,
1961 const gchar
*sess_id
, const gchar
*who
)
1964 if (sess_id
!= NULL
&& who
== NULL
) {
1965 PurpleMediaBackendFs2Session
*session
=
1966 get_session(self
, sess_id
);
1967 return (session
!= NULL
) ? session
->tee
: NULL
;
1968 } else if (sess_id
!= NULL
&& who
!= NULL
) {
1969 PurpleMediaBackendFs2Stream
*stream
=
1970 get_stream(self
, sess_id
, who
);
1971 return (stream
!= NULL
) ? stream
->tee
: NULL
;
1975 g_return_val_if_reached(NULL
);
1979 purple_media_backend_fs2_set_input_volume(PurpleMediaBackendFs2
*self
,
1980 const gchar
*sess_id
, double level
)
1983 PurpleMediaBackendFs2Private
*priv
;
1986 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
1988 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
1990 purple_prefs_set_int("/purple/media/audio/volume/input", level
);
1992 if (sess_id
== NULL
)
1993 sessions
= g_hash_table_get_values(priv
->sessions
);
1995 sessions
= g_list_append(NULL
, get_session(self
, sess_id
));
1997 for (; sessions
; sessions
= g_list_delete_link(sessions
, sessions
)) {
1998 PurpleMediaBackendFs2Session
*session
= sessions
->data
;
2000 if (session
->type
& PURPLE_MEDIA_SEND_AUDIO
) {
2001 gchar
*name
= g_strdup_printf("volume_%s",
2003 GstElement
*volume
= gst_bin_get_by_name(
2004 GST_BIN(priv
->confbin
), name
);
2006 g_object_set(volume
, "volume", level
/10.0, NULL
);
2013 purple_media_backend_fs2_set_output_volume(PurpleMediaBackendFs2
*self
,
2014 const gchar
*sess_id
, const gchar
*who
, double level
)
2017 PurpleMediaBackendFs2Private
*priv
;
2020 g_return_if_fail(PURPLE_IS_MEDIA_BACKEND_FS2(self
));
2022 priv
= PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self
);
2024 purple_prefs_set_int("/purple/media/audio/volume/output", level
);
2026 streams
= get_streams(self
, sess_id
, who
);
2028 for (; streams
; streams
= g_list_delete_link(streams
, streams
)) {
2029 PurpleMediaBackendFs2Stream
*stream
= streams
->data
;
2031 if (stream
->session
->type
& PURPLE_MEDIA_RECV_AUDIO
2032 && GST_IS_ELEMENT(stream
->volume
)) {
2033 g_object_set(stream
->volume
, "volume",
2039 #endif /* USE_GSTREAMER */