6 * Copyright (C) 2010-2015 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #include "glib/gstdio.h"
35 #include "sipe-common.h"
37 #include "mediamanager.h"
41 /* wrappers for write() & friends for socket handling */
42 #include "win32/win32dep.h"
45 #include "sipe-backend.h"
46 #include "sipe-core.h"
48 #include "purple-private.h"
51 * GStreamer interfaces fail to compile on ARM architecture with -Wcast-align
53 * Diagnostic #pragma was added in GCC 4.2.0
56 #if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || (__GNUC__ >= 5)
57 #if defined(__ARMEL__) || defined(__ARMEB__) || defined(__mips__) || defined(__sparc__) || (defined(__powerpc__) && defined(__NO_FPRS__))
58 #pragma GCC diagnostic ignored "-Wcast-align"
63 #include "media-gst.h"
65 struct sipe_backend_media
{
68 * Number of media streams that were not yet locally accepted or rejected.
70 guint unconfirmed_streams
;
73 struct sipe_backend_media_stream
{
74 gboolean local_on_hold
;
75 gboolean remote_on_hold
;
77 gboolean initialized_cb_was_fired
;
80 #if PURPLE_VERSION_CHECK(3,0,0)
81 #define SIPE_RELAYS_G_TYPE G_TYPE_PTR_ARRAY
83 #define SIPE_RELAYS_G_TYPE G_TYPE_VALUE_ARRAY
87 sipe_backend_media_stream_free(struct sipe_backend_media_stream
*stream
)
92 static PurpleMediaSessionType
sipe_media_to_purple(SipeMediaType type
);
93 static PurpleMediaCandidateType
sipe_candidate_type_to_purple(SipeCandidateType type
);
94 static SipeCandidateType
purple_candidate_type_to_sipe(PurpleMediaCandidateType type
);
95 static PurpleMediaNetworkProtocol
sipe_network_protocol_to_purple(SipeNetworkProtocol proto
);
96 static SipeNetworkProtocol
purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto
);
99 maybe_signal_stream_initialized(struct sipe_media_call
*call
, gchar
*sessionid
)
101 if (call
->stream_initialized_cb
) {
102 struct sipe_media_stream
*stream
;
103 stream
= sipe_core_media_get_stream_by_id(call
, sessionid
);
105 if (sipe_backend_stream_initialized(call
, stream
) &&
106 !stream
->backend_private
->initialized_cb_was_fired
) {
107 call
->stream_initialized_cb(call
, stream
);
108 stream
->backend_private
->initialized_cb_was_fired
= TRUE
;
114 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
116 SIPE_UNUSED_PARAMETER gchar
*participant
,
117 struct sipe_media_call
*call
)
119 maybe_signal_stream_initialized(call
, sessionid
);
123 on_codecs_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
125 struct sipe_media_call
*call
)
127 maybe_signal_stream_initialized(call
, sessionid
);
131 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
132 PurpleMediaState state
,
135 struct sipe_media_call
*call
)
137 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state
, sessionid
, participant
);
138 if (state
== PURPLE_MEDIA_STATE_END
) {
139 if (sessionid
&& participant
) {
140 struct sipe_media_stream
*stream
=
141 sipe_core_media_get_stream_by_id(call
, sessionid
);
143 call
->stream_end_cb(call
, stream
);
145 } else if (!sessionid
&& !participant
&& call
->media_end_cb
) {
146 call
->media_end_cb(call
);
152 capture_pipeline(const gchar
*label
) {
153 PurpleMediaManager
*manager
= purple_media_manager_get();
154 GstElement
*pipeline
= purple_media_manager_get_pipeline(manager
);
155 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline
), GST_DEBUG_GRAPH_SHOW_ALL
, label
);
159 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
, gchar
*message
,
160 struct sipe_media_call
*call
)
162 capture_pipeline("ERROR");
165 call
->error_cb(call
, message
);
169 on_stream_info_cb(PurpleMedia
*media
,
170 PurpleMediaInfoType type
,
174 struct sipe_media_call
*call
)
176 if (type
== PURPLE_MEDIA_INFO_ACCEPT
) {
177 if (call
->call_accept_cb
&& !sessionid
&& !participant
)
178 call
->call_accept_cb(call
, local
);
179 else if (sessionid
&& participant
) {
180 struct sipe_media_stream
*stream
;
181 stream
= sipe_core_media_get_stream_by_id(call
, sessionid
);
183 if (!stream
->backend_private
->accepted
&& local
)
184 --call
->backend_private
->unconfirmed_streams
;
185 stream
->backend_private
->accepted
= TRUE
;
188 } else if (type
== PURPLE_MEDIA_INFO_HOLD
|| type
== PURPLE_MEDIA_INFO_UNHOLD
) {
190 gboolean state
= (type
== PURPLE_MEDIA_INFO_HOLD
);
193 // Hold specific stream
194 struct sipe_media_stream
*stream
;
195 stream
= sipe_core_media_get_stream_by_id(call
, sessionid
);
198 stream
->backend_private
->local_on_hold
= state
;
200 stream
->backend_private
->remote_on_hold
= state
;
203 GList
*session_ids
= purple_media_get_session_ids(media
);
205 for (; session_ids
; session_ids
= session_ids
->next
) {
206 struct sipe_media_stream
*stream
=
207 sipe_core_media_get_stream_by_id(call
, session_ids
->data
);
210 stream
->backend_private
->local_on_hold
= state
;
212 stream
->backend_private
->remote_on_hold
= state
;
215 g_list_free(session_ids
);
218 if (call
->call_hold_cb
)
219 call
->call_hold_cb(call
, local
, state
);
220 } else if (type
== PURPLE_MEDIA_INFO_HANGUP
|| type
== PURPLE_MEDIA_INFO_REJECT
) {
221 if (!sessionid
&& !participant
) {
222 if (type
== PURPLE_MEDIA_INFO_HANGUP
&& call
->call_hangup_cb
)
223 call
->call_hangup_cb(call
, local
);
224 else if (type
== PURPLE_MEDIA_INFO_REJECT
&& call
->call_reject_cb
&& !local
)
225 call
->call_reject_cb(call
, local
);
226 } else if (sessionid
&& participant
) {
227 struct sipe_media_stream
*stream
;
228 stream
= sipe_core_media_get_stream_by_id(call
, sessionid
);
231 if (local
&& --call
->backend_private
->unconfirmed_streams
== 0 &&
232 call
->call_reject_cb
)
233 call
->call_reject_cb(call
, local
);
240 on_candidate_pair_established_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
241 SIPE_UNUSED_PARAMETER
const gchar
*sessionid
,
242 SIPE_UNUSED_PARAMETER
const gchar
*participant
,
243 SIPE_UNUSED_PARAMETER PurpleMediaCandidate
*local_candidate
,
244 SIPE_UNUSED_PARAMETER PurpleMediaCandidate
*remote_candidate
,
245 SIPE_UNUSED_PARAMETER
struct sipe_media_call
*call
)
247 #ifdef HAVE_PURPLE_NEW_TCP_ENUMS
248 if (purple_media_candidate_get_protocol(local_candidate
) != PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
) {
249 purple_media_set_send_rtcp_mux(media
, sessionid
, participant
, TRUE
);
254 struct sipe_backend_media
*
255 sipe_backend_media_new(struct sipe_core_public
*sipe_public
,
256 struct sipe_media_call
*call
,
257 const gchar
*participant
,
260 struct sipe_backend_media
*media
= g_new0(struct sipe_backend_media
, 1);
261 struct sipe_backend_private
*purple_private
= sipe_public
->backend_private
;
262 PurpleMediaManager
*manager
= purple_media_manager_get();
263 GstElement
*pipeline
;
265 media
->m
= purple_media_manager_create_media(manager
,
266 purple_private
->account
,
268 participant
, initiator
);
270 g_signal_connect(G_OBJECT(media
->m
), "candidates-prepared",
271 G_CALLBACK(on_candidates_prepared_cb
), call
);
272 g_signal_connect(G_OBJECT(media
->m
), "codecs-changed",
273 G_CALLBACK(on_codecs_changed_cb
), call
);
274 g_signal_connect(G_OBJECT(media
->m
), "stream-info",
275 G_CALLBACK(on_stream_info_cb
), call
);
276 g_signal_connect(G_OBJECT(media
->m
), "error",
277 G_CALLBACK(on_error_cb
), call
);
278 g_signal_connect(G_OBJECT(media
->m
), "state-changed",
279 G_CALLBACK(on_state_changed_cb
), call
);
280 g_signal_connect(G_OBJECT(media
->m
), "candidate-pair-established",
281 G_CALLBACK(on_candidate_pair_established_cb
), call
);
284 /* On error, the pipeline is no longer in PLAYING state and libpurple
285 * will not switch it back to PLAYING, preventing any more calls until
286 * application restart. We switch the state ourselves here to negate
287 * effect of any error in previous call (if any). */
288 pipeline
= purple_media_manager_get_pipeline(manager
);
289 gst_element_set_state(pipeline
, GST_STATE_PLAYING
);
295 sipe_backend_media_free(struct sipe_backend_media
*media
)
301 sipe_backend_media_set_cname(struct sipe_backend_media
*media
, gchar
*cname
)
304 guint num_params
= 3;
305 GParameter
*params
= g_new0(GParameter
, num_params
);
306 params
[0].name
= "sdes-cname";
307 g_value_init(¶ms
[0].value
, G_TYPE_STRING
);
308 g_value_set_string(¶ms
[0].value
, cname
);
309 params
[1].name
= "sdes-name";
310 g_value_init(¶ms
[1].value
, G_TYPE_STRING
);
311 params
[2].name
= "sdes-tool";
312 g_value_init(¶ms
[2].value
, G_TYPE_STRING
);
314 purple_media_set_params(media
->m
, num_params
, params
);
316 g_value_unset(¶ms
[0].value
);
321 #define FS_CODECS_CONF \
322 "# Automatically created by SIPE plugin\n" \
324 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
327 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
330 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
336 filename
= g_build_filename(purple_user_dir(), "fs-codec.conf", NULL
);
338 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
)) {
339 int fd
= g_open(filename
, O_CREAT
| O_WRONLY
, S_IRUSR
| S_IWUSR
);
340 gchar
*fs_codecs_conf
= FS_CODECS_CONF
;
341 if ((fd
< 0) || write(fd
, fs_codecs_conf
, strlen(fs_codecs_conf
)) == -1)
342 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
351 append_relay(struct sipe_backend_media_relays
*relay_info
, const gchar
*ip
,
352 guint port
, gchar
*type
, gchar
*username
, gchar
*password
)
354 GstStructure
*gst_relay_info
;
356 gst_relay_info
= gst_structure_new("relay-info",
357 "ip", G_TYPE_STRING
, ip
,
358 "port", G_TYPE_UINT
, port
,
359 "relay-type", G_TYPE_STRING
, type
,
360 "username", G_TYPE_STRING
, username
,
361 "password", G_TYPE_STRING
, password
,
364 if (gst_relay_info
) {
365 #if PURPLE_VERSION_CHECK(3,0,0)
366 g_ptr_array_add((GPtrArray
*)relay_info
, gst_relay_info
);
369 memset(&value
, 0, sizeof(GValue
));
370 g_value_init(&value
, GST_TYPE_STRUCTURE
);
371 gst_value_set_structure(&value
, gst_relay_info
);
373 g_value_array_append((GValueArray
*)relay_info
, &value
);
374 gst_structure_free(gst_relay_info
);
379 struct sipe_backend_media_relays
*
380 sipe_backend_media_relays_convert(GSList
*media_relays
, gchar
*username
, gchar
*password
)
382 struct sipe_backend_media_relays
*relay_info
;
384 relay_info
= (struct sipe_backend_media_relays
*)
385 #if PURPLE_VERSION_CHECK(3,0,0)
386 g_ptr_array_new_with_free_func((GDestroyNotify
) gst_structure_free
);
388 g_value_array_new(0);
391 for (; media_relays
; media_relays
= media_relays
->next
) {\
392 struct sipe_media_relay
*relay
= media_relays
->data
;
394 /* Skip relays where IP could not be resolved. */
395 if (!relay
->hostname
)
398 if (relay
->udp_port
!= 0)
399 append_relay(relay_info
, relay
->hostname
, relay
->udp_port
,
400 "udp", username
, password
);
402 #ifdef HAVE_PURPLE_NEW_TCP_ENUMS
403 if (relay
->tcp_port
!= 0) {
405 if (relay
->tcp_port
== 443)
407 append_relay(relay_info
, relay
->hostname
, relay
->tcp_port
,
408 type
, username
, password
);
417 sipe_backend_media_relays_free(struct sipe_backend_media_relays
*media_relays
)
419 #if !PURPLE_VERSION_CHECK(3,0,0)
420 g_value_array_free((GValueArray
*)media_relays
);
422 g_ptr_array_unref((GPtrArray
*)media_relays
);
426 struct sipe_backend_media_stream
*
427 sipe_backend_media_add_stream(struct sipe_media_call
*call
,
429 const gchar
*participant
,
431 SipeIceVersion ice_version
,
433 struct sipe_backend_media_relays
*media_relays
)
435 struct sipe_backend_media
*media
= call
->backend_private
;
436 struct sipe_backend_media_stream
*stream
= NULL
;
437 PurpleMediaSessionType prpl_type
= sipe_media_to_purple(type
);
438 // Preallocate enough space for all potential parameters to fit.
439 GParameter
*params
= g_new0(GParameter
, 5);
440 guint params_cnt
= 0;
442 GValue
*relay_info
= NULL
;
444 if (ice_version
!= SIPE_ICE_NO_ICE
) {
445 transmitter
= "nice";
447 params
[params_cnt
].name
= "compatibility-mode";
448 g_value_init(¶ms
[params_cnt
].value
, G_TYPE_UINT
);
449 g_value_set_uint(¶ms
[params_cnt
].value
,
450 ice_version
== SIPE_ICE_DRAFT_6
?
451 NICE_COMPATIBILITY_OC2007
:
452 NICE_COMPATIBILITY_OC2007R2
);
456 params
[params_cnt
].name
= "relay-info";
457 g_value_init(¶ms
[params_cnt
].value
, SIPE_RELAYS_G_TYPE
);
458 g_value_set_boxed(¶ms
[params_cnt
].value
, media_relays
);
459 relay_info
= ¶ms
[params_cnt
].value
;
463 // TODO: session naming here, Communicator needs audio/video
464 transmitter
= "rawudp";
465 //sessionid = "sipe-voice-rawudp";
468 ensure_codecs_conf();
470 if (purple_media_add_stream(media
->m
, id
, participant
, prpl_type
,
471 initiator
, transmitter
, params_cnt
,
473 stream
= g_new0(struct sipe_backend_media_stream
, 1);
474 stream
->initialized_cb_was_fired
= FALSE
;
477 ++media
->unconfirmed_streams
;
481 g_value_unset(relay_info
);
490 sipe_backend_media_stream_end(struct sipe_media_call
*media
,
491 struct sipe_media_stream
*stream
)
493 purple_media_end(media
->backend_private
->m
, stream
->id
, NULL
);
497 sipe_backend_media_add_remote_candidates(struct sipe_media_call
*media
,
498 struct sipe_media_stream
*stream
,
501 GList
*udp_candidates
= NULL
;
503 #ifdef HAVE_PURPLE_NEW_TCP_ENUMS
505 PurpleMediaCandidate
*candidate
= candidates
->data
;
506 PurpleMediaNetworkProtocol proto
;
508 proto
= purple_media_candidate_get_protocol(candidate
);
509 if (proto
== PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
)
510 udp_candidates
= g_list_append(udp_candidates
, candidate
);
512 candidates
= candidates
->next
;
515 candidates
= udp_candidates
;
518 purple_media_add_remote_candidates(media
->backend_private
->m
,
519 stream
->id
, media
->with
, candidates
);
521 g_list_free(udp_candidates
);
524 gboolean
sipe_backend_media_is_initiator(struct sipe_media_call
*media
,
525 struct sipe_media_stream
*stream
)
527 return purple_media_is_initiator(media
->backend_private
->m
,
528 stream
? stream
->id
: NULL
,
529 stream
? media
->with
: NULL
);
532 gboolean
sipe_backend_media_accepted(struct sipe_backend_media
*media
)
534 return purple_media_accepted(media
->m
, NULL
, NULL
);
538 sipe_backend_stream_initialized(struct sipe_media_call
*media
,
539 struct sipe_media_stream
*stream
)
541 g_return_val_if_fail(media
, FALSE
);
542 g_return_val_if_fail(stream
, FALSE
);
544 if (purple_media_candidates_prepared(media
->backend_private
->m
,
545 stream
->id
, media
->with
)) {
547 codecs
= purple_media_get_codecs(media
->backend_private
->m
,
550 purple_media_codec_list_free(codecs
);
558 duplicate_tcp_candidates(GList
*candidates
)
561 GList
*result
= NULL
;
563 for (i
= candidates
; i
; i
= i
->next
) {
564 PurpleMediaCandidate
*candidate
= i
->data
;
565 PurpleMediaNetworkProtocol protocol
=
566 purple_media_candidate_get_protocol(candidate
);
568 purple_media_candidate_get_component_id(candidate
);
570 if (protocol
!= PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
) {
571 PurpleMediaCandidate
*c2
;
573 if (component_id
!= PURPLE_MEDIA_COMPONENT_RTP
) {
574 /* Ignore TCP candidates for other than
575 * the first component. */
576 g_object_unref(candidate
);
580 c2
= purple_media_candidate_copy(candidate
);
582 "component-id", PURPLE_MEDIA_COMPONENT_RTCP
,
584 result
= g_list_append(result
, c2
);
587 result
= g_list_append(result
, candidate
);
590 g_list_free(candidates
);
596 sipe_backend_media_get_active_local_candidates(struct sipe_media_call
*media
,
597 struct sipe_media_stream
*stream
)
599 GList
*candidates
= purple_media_get_active_local_candidates(
600 media
->backend_private
->m
, stream
->id
, media
->with
);
601 return duplicate_tcp_candidates(candidates
);
605 sipe_backend_media_get_active_remote_candidates(struct sipe_media_call
*media
,
606 struct sipe_media_stream
*stream
)
608 GList
*candidates
= purple_media_get_active_remote_candidates(
609 media
->backend_private
->m
, stream
->id
, media
->with
);
610 return duplicate_tcp_candidates(candidates
);
615 sipe_backend_media_set_encryption_keys(struct sipe_media_call
*media
,
616 struct sipe_media_stream
*stream
,
617 const guchar
*encryption_key
,
618 const guchar
*decryption_key
)
620 purple_media_set_encryption_parameters(media
->backend_private
->m
,
624 (gchar
*)encryption_key
, SIPE_SRTP_KEY_LEN
);
625 purple_media_set_decryption_parameters(media
->backend_private
->m
,
626 stream
->id
, media
->with
,
629 (gchar
*)decryption_key
, SIPE_SRTP_KEY_LEN
);
633 sipe_backend_media_set_encryption_keys(SIPE_UNUSED_PARAMETER
struct sipe_media_call
*media
,
634 SIPE_UNUSED_PARAMETER
struct sipe_media_stream
*stream
,
635 SIPE_UNUSED_PARAMETER
const guchar
*encryption_key
,
636 SIPE_UNUSED_PARAMETER
const guchar
*decryption_key
)
640 void sipe_backend_stream_hold(struct sipe_media_call
*media
,
641 struct sipe_media_stream
*stream
,
644 purple_media_stream_info(media
->backend_private
->m
, PURPLE_MEDIA_INFO_HOLD
,
645 stream
->id
, media
->with
, local
);
648 void sipe_backend_stream_unhold(struct sipe_media_call
*media
,
649 struct sipe_media_stream
*stream
,
652 purple_media_stream_info(media
->backend_private
->m
, PURPLE_MEDIA_INFO_UNHOLD
,
653 stream
->id
, media
->with
, local
);
656 gboolean
sipe_backend_stream_is_held(struct sipe_media_stream
*stream
)
658 g_return_val_if_fail(stream
, FALSE
);
660 return stream
->backend_private
->local_on_hold
||
661 stream
->backend_private
->remote_on_hold
;
664 struct sipe_backend_codec
*
665 sipe_backend_codec_new(int id
, const char *name
, SipeMediaType type
, guint clock_rate
)
667 return (struct sipe_backend_codec
*)purple_media_codec_new(id
, name
,
668 sipe_media_to_purple(type
),
673 sipe_backend_codec_free(struct sipe_backend_codec
*codec
)
676 g_object_unref(codec
);
680 sipe_backend_codec_get_id(struct sipe_backend_codec
*codec
)
682 return purple_media_codec_get_id((PurpleMediaCodec
*)codec
);
686 sipe_backend_codec_get_name(struct sipe_backend_codec
*codec
)
688 /* Not explicitly documented, but return value must be g_free()'d */
689 return purple_media_codec_get_encoding_name((PurpleMediaCodec
*)codec
);
693 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec
*codec
)
695 return purple_media_codec_get_clock_rate((PurpleMediaCodec
*)codec
);
699 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec
*codec
,
700 const gchar
*name
, const gchar
*value
)
702 purple_media_codec_add_optional_parameter((PurpleMediaCodec
*)codec
, name
, value
);
706 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec
*codec
)
708 return purple_media_codec_get_optional_parameters((PurpleMediaCodec
*)codec
);
712 sipe_backend_set_remote_codecs(struct sipe_media_call
*media
,
713 struct sipe_media_stream
*stream
,
716 return purple_media_set_remote_codecs(media
->backend_private
->m
,
717 stream
->id
, media
->with
, codecs
);
721 sipe_backend_get_local_codecs(struct sipe_media_call
*media
,
722 struct sipe_media_stream
*stream
)
724 GList
*codecs
= purple_media_get_codecs(media
->backend_private
->m
,
727 gboolean is_conference
= (g_strstr_len(media
->with
, strlen(media
->with
),
728 "app:conf:audio-video:") != NULL
);
731 * Do not announce Theora. Its optional parameters are too long,
732 * Communicator rejects such SDP message and does not support the codec
735 * For some yet unknown reason, A/V conferencing server does not accept
736 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
737 * we are able to decode incoming SIREN from server and with MSOC
738 * client, bidirectional call using the codec works. Until resolved,
739 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
740 * seems to work properly in this scenario.
743 PurpleMediaCodec
*codec
= i
->data
;
744 gchar
*encoding_name
= purple_media_codec_get_encoding_name(codec
);
746 if (sipe_strequal(encoding_name
,"THEORA") ||
747 (is_conference
&& sipe_strequal(encoding_name
,"SIREN"))) {
749 g_object_unref(codec
);
751 codecs
= g_list_delete_link(codecs
, i
);
756 g_free(encoding_name
);
762 struct sipe_backend_candidate
*
763 sipe_backend_candidate_new(const gchar
*foundation
,
764 SipeComponentType component
,
765 SipeCandidateType type
, SipeNetworkProtocol proto
,
766 const gchar
*ip
, guint port
,
767 const gchar
*username
,
768 const gchar
*password
)
770 PurpleMediaCandidate
*c
= purple_media_candidate_new(
771 /* Libnice and Farsight rely on non-NULL foundation to
772 * distinguish between candidates of a component. When NULL
773 * foundation is passed (ie. ICE draft 6 does not use foudation),
774 * use username instead. If no foundation is provided, Farsight
775 * may signal an active candidate different from the one actually
776 * in use. See Farsight's agent_new_selected_pair() in
777 * fs-nice-stream-transmitter.h where first candidate in the
778 * remote list is always selected when no foundation. */
779 foundation
? foundation
: username
,
781 sipe_candidate_type_to_purple(type
),
782 sipe_network_protocol_to_purple(proto
),
785 g_object_set(c
, "username", username
, "password", password
, NULL
);
786 return (struct sipe_backend_candidate
*)c
;
790 sipe_backend_candidate_free(struct sipe_backend_candidate
*candidate
)
793 g_object_unref(candidate
);
797 sipe_backend_candidate_get_username(struct sipe_backend_candidate
*candidate
)
799 /* Not explicitly documented, but return value must be g_free()'d */
800 return purple_media_candidate_get_username((PurpleMediaCandidate
*)candidate
);
804 sipe_backend_candidate_get_password(struct sipe_backend_candidate
*candidate
)
806 /* Not explicitly documented, but return value must be g_free()'d */
807 return purple_media_candidate_get_password((PurpleMediaCandidate
*)candidate
);
811 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate
*candidate
)
813 /* Not explicitly documented, but return value must be g_free()'d */
814 return purple_media_candidate_get_foundation((PurpleMediaCandidate
*)candidate
);
818 sipe_backend_candidate_get_ip(struct sipe_backend_candidate
*candidate
)
820 /* Not explicitly documented, but return value must be g_free()'d */
821 return purple_media_candidate_get_ip((PurpleMediaCandidate
*)candidate
);
825 sipe_backend_candidate_get_port(struct sipe_backend_candidate
*candidate
)
827 return purple_media_candidate_get_port((PurpleMediaCandidate
*)candidate
);
831 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate
*candidate
)
833 /* Not explicitly documented, but return value must be g_free()'d */
834 return purple_media_candidate_get_base_ip((PurpleMediaCandidate
*)candidate
);
838 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate
*candidate
)
840 return purple_media_candidate_get_base_port((PurpleMediaCandidate
*)candidate
);
844 sipe_backend_candidate_get_priority(struct sipe_backend_candidate
*candidate
)
846 return purple_media_candidate_get_priority((PurpleMediaCandidate
*)candidate
);
850 sipe_backend_candidate_set_priority(struct sipe_backend_candidate
*candidate
, guint32 priority
)
852 g_object_set(candidate
, "priority", priority
, NULL
);
856 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate
*candidate
)
858 return purple_media_candidate_get_component_id((PurpleMediaCandidate
*)candidate
);
862 sipe_backend_candidate_get_type(struct sipe_backend_candidate
*candidate
)
864 PurpleMediaCandidateType type
=
865 purple_media_candidate_get_candidate_type((PurpleMediaCandidate
*)candidate
);
866 return purple_candidate_type_to_sipe(type
);
870 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate
*candidate
)
872 PurpleMediaNetworkProtocol proto
=
873 purple_media_candidate_get_protocol((PurpleMediaCandidate
*)candidate
);
874 return purple_network_protocol_to_sipe(proto
);
878 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key
,
882 GList
*entry
= value
;
883 GList
**candidates
= user_data
;
885 g_object_unref(entry
->data
);
886 *candidates
= g_list_delete_link(*candidates
, entry
);
890 ensure_candidate_pairs(GList
*candidates
)
892 GHashTable
*lone_cand_links
;
895 lone_cand_links
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
897 for (i
= candidates
; i
; i
= i
->next
) {
898 PurpleMediaCandidate
*c
= i
->data
;
899 gchar
*foundation
= purple_media_candidate_get_foundation(c
);
901 if (g_hash_table_lookup(lone_cand_links
, foundation
)) {
902 g_hash_table_remove(lone_cand_links
, foundation
);
905 g_hash_table_insert(lone_cand_links
, foundation
, i
);
909 g_hash_table_foreach(lone_cand_links
, remove_lone_candidate_cb
, &candidates
);
910 g_hash_table_destroy(lone_cand_links
);
916 sipe_backend_get_local_candidates(struct sipe_media_call
*media
,
917 struct sipe_media_stream
*stream
)
920 purple_media_get_local_candidates(media
->backend_private
->m
,
923 candidates
= duplicate_tcp_candidates(candidates
);
926 * Sometimes purple will not return complete list of candidates, even
927 * after "candidates-prepared" signal is emitted. This is a feature of
928 * libnice, namely affecting candidates discovered via UPnP. Nice does
929 * not wait until discovery is finished and can signal end of candidate
930 * gathering before all responses from UPnP enabled gateways are received.
932 * Remove any incomplete RTP+RTCP candidate pairs from the list.
934 candidates
= ensure_candidate_pairs(candidates
);
939 sipe_backend_media_accept(struct sipe_backend_media
*media
, gboolean local
)
942 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_ACCEPT
,
947 sipe_backend_media_hangup(struct sipe_backend_media
*media
, gboolean local
)
950 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_HANGUP
,
955 sipe_backend_media_reject(struct sipe_backend_media
*media
, gboolean local
)
958 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_REJECT
,
962 static PurpleMediaSessionType
sipe_media_to_purple(SipeMediaType type
)
965 case SIPE_MEDIA_AUDIO
: return PURPLE_MEDIA_AUDIO
;
966 case SIPE_MEDIA_VIDEO
: return PURPLE_MEDIA_VIDEO
;
967 default: return PURPLE_MEDIA_NONE
;
971 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
974 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
975 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
976 default: return SIPE_MEDIA_AUDIO;
980 static PurpleMediaCandidateType
981 sipe_candidate_type_to_purple(SipeCandidateType type
)
984 case SIPE_CANDIDATE_TYPE_HOST
: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
985 case SIPE_CANDIDATE_TYPE_RELAY
: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
;
986 case SIPE_CANDIDATE_TYPE_SRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
;
987 case SIPE_CANDIDATE_TYPE_PRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
;
988 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
992 static SipeCandidateType
993 purple_candidate_type_to_sipe(PurpleMediaCandidateType type
)
996 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST
: return SIPE_CANDIDATE_TYPE_HOST
;
997 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
: return SIPE_CANDIDATE_TYPE_RELAY
;
998 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
: return SIPE_CANDIDATE_TYPE_SRFLX
;
999 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
: return SIPE_CANDIDATE_TYPE_PRFLX
;
1000 default: return SIPE_CANDIDATE_TYPE_HOST
;
1004 static PurpleMediaNetworkProtocol
1005 sipe_network_protocol_to_purple(SipeNetworkProtocol proto
)
1008 #ifdef HAVE_PURPLE_NEW_TCP_ENUMS
1009 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
1010 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE
;
1011 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
:
1012 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE
;
1013 case SIPE_NETWORK_PROTOCOL_TCP_SO
:
1014 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_SO
;
1016 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
1017 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
:
1018 case SIPE_NETWORK_PROTOCOL_TCP_SO
:
1019 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
;
1022 case SIPE_NETWORK_PROTOCOL_UDP
:
1023 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
;
1027 static SipeNetworkProtocol
1028 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto
)
1031 #ifdef HAVE_PURPLE_NEW_TCP_ENUMS
1032 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE
:
1033 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
1034 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE
:
1035 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
1036 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_SO
:
1037 return SIPE_NETWORK_PROTOCOL_TCP_SO
;
1039 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
:
1040 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
1043 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
:
1044 return SIPE_NETWORK_PROTOCOL_UDP
;
1049 SipeEncryptionPolicy
1050 sipe_backend_media_get_encryption_policy(struct sipe_core_public
*sipe_public
)
1052 PurpleAccount
*account
= sipe_public
->backend_private
->account
;
1054 const char *policy
=
1055 purple_account_get_string(account
, "encryption-policy",
1058 if (sipe_strequal(policy
, "disabled")) {
1059 return SIPE_ENCRYPTION_POLICY_REJECTED
;
1060 } else if (sipe_strequal(policy
, "optional")) {
1061 return SIPE_ENCRYPTION_POLICY_OPTIONAL
;
1062 } else if (sipe_strequal(policy
, "required")) {
1063 return SIPE_ENCRYPTION_POLICY_REQUIRED
;
1065 return SIPE_ENCRYPTION_POLICY_OBEY_SERVER
;
1069 SipeEncryptionPolicy
1070 sipe_backend_media_get_encryption_policy(SIPE_UNUSED_PARAMETER
struct sipe_core_public
*sipe_public
)
1072 return SIPE_ENCRYPTION_POLICY_REJECTED
;