6 * Copyright (C) 2010-2014 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
55 #if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 2)
56 #if defined(__ARMEL__) || defined(__ARMEB__) || defined(__mips__) || defined(__sparc__) || (defined(__powerpc__) && defined(__NO_FPRS__))
57 #pragma GCC diagnostic ignored "-Wcast-align"
61 #include "media-gst.h"
63 struct sipe_backend_media
{
64 struct sipe_media_call
*call
;
68 * Number of media streams that were not yet locally accepted or rejected.
70 guint unconfirmed_streams
;
73 struct sipe_backend_stream
{
76 gboolean local_on_hold
;
77 gboolean remote_on_hold
;
79 gboolean initialized_cb_was_fired
;
83 backend_stream_free(struct sipe_backend_stream
*stream
)
86 g_free(stream
->sessionid
);
87 g_free(stream
->participant
);
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_backend_stream
*stream
;
103 stream
= sipe_backend_media_get_stream_by_id(call
->backend_private
, sessionid
);
105 if (sipe_backend_stream_initialized(call
->backend_private
, stream
) &&
106 !stream
->initialized_cb_was_fired
) {
107 call
->stream_initialized_cb(call
, stream
);
108 stream
->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 !sessionid
&& !participant
&& call
->media_end_cb
)
140 call
->media_end_cb(call
);
144 capture_pipeline(const gchar
*label
) {
145 PurpleMediaManager
*manager
= purple_media_manager_get();
146 GstElement
*pipeline
= purple_media_manager_get_pipeline(manager
);
147 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline
), GST_DEBUG_GRAPH_SHOW_ALL
, label
);
151 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
, gchar
*message
,
152 struct sipe_media_call
*call
)
154 capture_pipeline("ERROR");
157 call
->error_cb(call
, message
);
161 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
162 PurpleMediaInfoType type
,
166 struct sipe_media_call
*call
)
168 if (type
== PURPLE_MEDIA_INFO_ACCEPT
) {
169 if (call
->call_accept_cb
&& !sessionid
&& !participant
)
170 call
->call_accept_cb(call
, local
);
171 else if (sessionid
&& participant
) {
172 struct sipe_backend_stream
*stream
;
173 stream
= sipe_backend_media_get_stream_by_id(call
->backend_private
,
176 if (!stream
->accepted
&& local
)
177 --call
->backend_private
->unconfirmed_streams
;
178 stream
->accepted
= TRUE
;
181 } else if (type
== PURPLE_MEDIA_INFO_HOLD
|| type
== PURPLE_MEDIA_INFO_UNHOLD
) {
183 gboolean state
= (type
== PURPLE_MEDIA_INFO_HOLD
);
186 // Hold specific stream
187 struct sipe_backend_stream
*stream
;
188 stream
= sipe_backend_media_get_stream_by_id(call
->backend_private
,
192 stream
->local_on_hold
= state
;
194 stream
->remote_on_hold
= state
;
197 GSList
*i
= sipe_backend_media_get_streams(call
->backend_private
);
198 for (; i
; i
= i
->next
) {
199 struct sipe_backend_stream
*stream
= i
->data
;
202 stream
->local_on_hold
= state
;
204 stream
->remote_on_hold
= state
;
208 if (call
->call_hold_cb
)
209 call
->call_hold_cb(call
, local
, state
);
210 } else if (type
== PURPLE_MEDIA_INFO_HANGUP
|| type
== PURPLE_MEDIA_INFO_REJECT
) {
211 if (!sessionid
&& !participant
) {
212 if (type
== PURPLE_MEDIA_INFO_HANGUP
&& call
->call_hangup_cb
)
213 call
->call_hangup_cb(call
, local
);
214 else if (type
== PURPLE_MEDIA_INFO_REJECT
&& call
->call_reject_cb
&& !local
)
215 call
->call_reject_cb(call
, local
);
216 } else if (sessionid
&& participant
) {
217 struct sipe_backend_stream
*stream
;
218 stream
= sipe_backend_media_get_stream_by_id(call
->backend_private
,
222 call
->backend_private
->streams
= g_slist_remove(call
->backend_private
->streams
, stream
);
223 backend_stream_free(stream
);
224 if (local
&& --call
->backend_private
->unconfirmed_streams
== 0 &&
225 call
->call_reject_cb
)
226 call
->call_reject_cb(call
, local
);
232 struct sipe_backend_media
*
233 sipe_backend_media_new(struct sipe_core_public
*sipe_public
,
234 struct sipe_media_call
*call
,
235 const gchar
*participant
,
238 struct sipe_backend_media
*media
= g_new0(struct sipe_backend_media
, 1);
239 struct sipe_backend_private
*purple_private
= sipe_public
->backend_private
;
240 PurpleMediaManager
*manager
= purple_media_manager_get();
241 GstElement
*pipeline
;
244 media
->m
= purple_media_manager_create_media(manager
,
245 purple_private
->account
,
247 participant
, initiator
);
249 g_signal_connect(G_OBJECT(media
->m
), "candidates-prepared",
250 G_CALLBACK(on_candidates_prepared_cb
), call
);
251 g_signal_connect(G_OBJECT(media
->m
), "codecs-changed",
252 G_CALLBACK(on_codecs_changed_cb
), call
);
253 g_signal_connect(G_OBJECT(media
->m
), "stream-info",
254 G_CALLBACK(on_stream_info_cb
), call
);
255 g_signal_connect(G_OBJECT(media
->m
), "error",
256 G_CALLBACK(on_error_cb
), call
);
257 g_signal_connect(G_OBJECT(media
->m
), "state-changed",
258 G_CALLBACK(on_state_changed_cb
), call
);
260 /* On error, the pipeline is no longer in PLAYING state and libpurple
261 * will not switch it back to PLAYING, preventing any more calls until
262 * application restart. We switch the state ourselves here to negate
263 * effect of any error in previous call (if any). */
264 pipeline
= purple_media_manager_get_pipeline(manager
);
265 gst_element_set_state(pipeline
, GST_STATE_PLAYING
);
271 sipe_backend_media_free(struct sipe_backend_media
*media
)
274 GSList
*stream
= media
->streams
;
275 PurpleMediaManager
*manager
= purple_media_manager_get();
277 g_signal_handlers_disconnect_by_func(G_OBJECT(media
->m
),
278 G_CALLBACK(on_candidates_prepared_cb
),
280 g_signal_handlers_disconnect_by_func(G_OBJECT(media
->m
),
281 G_CALLBACK(on_codecs_changed_cb
),
283 g_signal_handlers_disconnect_by_func(G_OBJECT(media
->m
),
284 G_CALLBACK(on_stream_info_cb
),
286 g_signal_handlers_disconnect_by_func(G_OBJECT(media
->m
),
287 G_CALLBACK(on_error_cb
),
289 g_signal_handlers_disconnect_by_func(G_OBJECT(media
->m
),
290 G_CALLBACK(on_state_changed_cb
),
292 purple_media_manager_remove_media(manager
, media
->m
);
294 for (; stream
; stream
= g_slist_delete_link(stream
, stream
))
295 backend_stream_free(stream
->data
);
302 sipe_backend_media_set_cname(struct sipe_backend_media
*media
, gchar
*cname
)
305 guint num_params
= 3;
306 GParameter
*params
= g_new0(GParameter
, num_params
);
307 params
[0].name
= "sdes-cname";
308 g_value_init(¶ms
[0].value
, G_TYPE_STRING
);
309 g_value_set_string(¶ms
[0].value
, cname
);
310 params
[1].name
= "sdes-name";
311 g_value_init(¶ms
[1].value
, G_TYPE_STRING
);
312 params
[2].name
= "sdes-tool";
313 g_value_init(¶ms
[2].value
, G_TYPE_STRING
);
315 purple_media_set_params(media
->m
, num_params
, params
);
317 g_value_unset(¶ms
[0].value
);
322 #define FS_CODECS_CONF \
323 "# Automatically created by SIPE plugin\n" \
325 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
328 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
331 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
337 filename
= g_build_filename(purple_user_dir(), "fs-codec.conf", NULL
);
339 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
)) {
340 int fd
= g_open(filename
, O_CREAT
| O_WRONLY
, S_IRUSR
| S_IWUSR
);
341 gchar
*fs_codecs_conf
= FS_CODECS_CONF
;
342 if ((fd
< 0) || write(fd
, fs_codecs_conf
, strlen(fs_codecs_conf
)) == -1)
343 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
352 append_relay(GValueArray
*relay_info
, const gchar
*ip
, guint port
, gchar
*type
,
353 gchar
*username
, gchar
*password
)
356 GstStructure
*gst_relay_info
;
358 gst_relay_info
= gst_structure_new("relay-info",
359 "ip", G_TYPE_STRING
, ip
,
360 "port", G_TYPE_UINT
, port
,
361 "relay-type", G_TYPE_STRING
, type
,
362 "username", G_TYPE_STRING
, username
,
363 "password", G_TYPE_STRING
, password
,
366 if (gst_relay_info
) {
367 memset(&value
, 0, sizeof(GValue
));
368 g_value_init(&value
, GST_TYPE_STRUCTURE
);
369 gst_value_set_structure(&value
, gst_relay_info
);
371 g_value_array_append(relay_info
, &value
);
372 gst_structure_free(gst_relay_info
);
376 struct sipe_backend_media_relays
*
377 sipe_backend_media_relays_convert(GSList
*media_relays
, gchar
*username
, gchar
*password
)
379 GValueArray
*relay_info
= g_value_array_new(0);
381 for (; media_relays
; media_relays
= media_relays
->next
) {\
382 struct sipe_media_relay
*relay
= media_relays
->data
;
384 /* Skip relays where IP could not be resolved. */
385 if (!relay
->hostname
)
388 if (relay
->udp_port
!= 0)
389 append_relay(relay_info
, relay
->hostname
, relay
->udp_port
,
390 "udp", username
, password
);
393 if (relay
->tcp_port
!= 0)
394 append_relay(relay_info
, relay
->hostname
, relay
->tcp_port
,
395 "tcp", username
, password
);
399 return (struct sipe_backend_media_relays
*)relay_info
;
403 sipe_backend_media_relays_free(struct sipe_backend_media_relays
*media_relays
)
405 g_value_array_free((GValueArray
*)media_relays
);
409 stream_demultiplex_cb(const gchar
*buf
, SIPE_UNUSED_PARAMETER gpointer
*user_data
)
411 guint8 payload_type
= buf
[1] & 0x7F;
412 if (payload_type
>= 200 && payload_type
<=204) {
414 return PURPLE_MEDIA_COMPONENT_RTCP
;
417 return PURPLE_MEDIA_COMPONENT_RTP
;
421 struct sipe_backend_stream
*
422 sipe_backend_media_add_stream(struct sipe_backend_media
*media
,
424 const gchar
*participant
,
426 SipeIceVersion ice_version
,
428 struct sipe_backend_media_relays
*media_relays
)
430 struct sipe_backend_stream
*stream
= NULL
;
431 PurpleMediaSessionType prpl_type
= sipe_media_to_purple(type
);
432 GParameter
*params
= NULL
;
433 guint params_cnt
= 0;
436 if (ice_version
!= SIPE_ICE_NO_ICE
) {
437 transmitter
= "nice";
440 params
= g_new0(GParameter
, params_cnt
);
442 params
[0].name
= "compatibility-mode";
443 g_value_init(¶ms
[0].value
, G_TYPE_UINT
);
444 g_value_set_uint(¶ms
[0].value
,
445 ice_version
== SIPE_ICE_DRAFT_6
?
446 NICE_COMPATIBILITY_OC2007
:
447 NICE_COMPATIBILITY_OC2007R2
);
449 params
[1].name
= "transport-protocols";
450 g_value_init(¶ms
[1].value
, G_TYPE_UINT
);
452 g_value_set_uint(¶ms
[1].value
,
453 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
|
454 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE
|
455 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE
);
457 g_value_set_uint(¶ms
[1].value
,
458 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
);
461 params
[2].name
= "demultiplex-func";
462 g_value_init(¶ms
[2].value
, G_TYPE_POINTER
);
463 g_value_set_pointer(¶ms
[2].value
, stream_demultiplex_cb
);
466 params
[3].name
= "relay-info";
467 g_value_init(¶ms
[3].value
, G_TYPE_VALUE_ARRAY
);
468 g_value_set_boxed(¶ms
[3].value
, media_relays
);
472 // TODO: session naming here, Communicator needs audio/video
473 transmitter
= "rawudp";
474 //sessionid = "sipe-voice-rawudp";
476 /* To avoid Coverity FORWARD_NULL warning. params_cnt is
477 * still 0 so this is a no-op. libpurple API documentation
478 * doesn't specify if params can be NULL or not. */
479 params
= g_new0(GParameter
, 1);
482 ensure_codecs_conf();
484 if (purple_media_add_stream(media
->m
, id
, participant
, prpl_type
,
485 initiator
, transmitter
, params_cnt
,
487 stream
= g_new0(struct sipe_backend_stream
, 1);
488 stream
->sessionid
= g_strdup(id
);
489 stream
->participant
= g_strdup(participant
);
490 stream
->initialized_cb_was_fired
= FALSE
;
492 media
->streams
= g_slist_append(media
->streams
, stream
);
494 ++media
->unconfirmed_streams
;
497 if ((params_cnt
> 2) && media_relays
)
498 g_value_unset(¶ms
[3].value
);
506 sipe_backend_media_remove_stream(struct sipe_backend_media
*media
,
507 struct sipe_backend_stream
*stream
)
509 g_return_if_fail(media
&& stream
);
511 purple_media_end(media
->m
, stream
->sessionid
, NULL
);
512 media
->streams
= g_slist_remove(media
->streams
, stream
);
513 backend_stream_free(stream
);
516 GSList
*sipe_backend_media_get_streams(struct sipe_backend_media
*media
)
518 return media
->streams
;
521 struct sipe_backend_stream
*
522 sipe_backend_media_get_stream_by_id(struct sipe_backend_media
*media
,
526 for (i
= media
->streams
; i
; i
= i
->next
) {
527 struct sipe_backend_stream
*stream
= i
->data
;
528 if (sipe_strequal(stream
->sessionid
, id
))
535 sipe_backend_media_add_remote_candidates(struct sipe_backend_media
*media
,
536 struct sipe_backend_stream
*stream
,
539 GList
*udp_candidates
= NULL
;
543 PurpleMediaCandidate
*candidate
= candidates
->data
;
544 PurpleMediaNetworkProtocol proto
;
546 proto
= purple_media_candidate_get_protocol(candidate
);
547 if (proto
== PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
)
548 udp_candidates
= g_list_append(udp_candidates
, candidate
);
550 candidates
= candidates
->next
;
553 candidates
= udp_candidates
;
557 purple_media_add_remote_candidates(media
->m
, stream
->sessionid
,
558 stream
->participant
, candidates
);
560 g_list_free(udp_candidates
);
563 gboolean
sipe_backend_media_is_initiator(struct sipe_backend_media
*media
,
564 struct sipe_backend_stream
*stream
)
566 return purple_media_is_initiator(media
->m
,
567 stream
? stream
->sessionid
: NULL
,
568 stream
? stream
->participant
: NULL
);
571 gboolean
sipe_backend_media_accepted(struct sipe_backend_media
*media
)
573 return purple_media_accepted(media
->m
, NULL
, NULL
);
577 sipe_backend_stream_initialized(struct sipe_backend_media
*media
,
578 struct sipe_backend_stream
*stream
)
580 g_return_val_if_fail(media
, FALSE
);
581 g_return_val_if_fail(stream
, FALSE
);
583 if (purple_media_candidates_prepared(media
->m
,
585 stream
->participant
)) {
587 codecs
= purple_media_get_codecs(media
->m
, stream
->sessionid
);
589 purple_media_codec_list_free(codecs
);
597 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media
*media
,
598 struct sipe_backend_stream
*stream
)
600 return purple_media_get_active_local_candidates(media
->m
,
602 stream
->participant
);
606 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media
*media
,
607 struct sipe_backend_stream
*stream
)
609 return purple_media_get_active_remote_candidates(media
->m
,
611 stream
->participant
);
615 sipe_backend_stream_get_id(struct sipe_backend_stream
*stream
)
617 return stream
->sessionid
;
620 void sipe_backend_stream_hold(struct sipe_backend_media
*media
,
621 struct sipe_backend_stream
*stream
,
624 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_HOLD
,
625 stream
->sessionid
, stream
->participant
,
629 void sipe_backend_stream_unhold(struct sipe_backend_media
*media
,
630 struct sipe_backend_stream
*stream
,
633 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_UNHOLD
,
634 stream
->sessionid
, stream
->participant
,
638 gboolean
sipe_backend_stream_is_held(struct sipe_backend_stream
*stream
)
640 g_return_val_if_fail(stream
, FALSE
);
642 return stream
->local_on_hold
|| stream
->remote_on_hold
;
645 struct sipe_backend_codec
*
646 sipe_backend_codec_new(int id
, const char *name
, SipeMediaType type
, guint clock_rate
)
648 return (struct sipe_backend_codec
*)purple_media_codec_new(id
, name
,
649 sipe_media_to_purple(type
),
654 sipe_backend_codec_free(struct sipe_backend_codec
*codec
)
657 g_object_unref(codec
);
661 sipe_backend_codec_get_id(struct sipe_backend_codec
*codec
)
663 return purple_media_codec_get_id((PurpleMediaCodec
*)codec
);
667 sipe_backend_codec_get_name(struct sipe_backend_codec
*codec
)
669 /* Not explicitly documented, but return value must be g_free()'d */
670 return purple_media_codec_get_encoding_name((PurpleMediaCodec
*)codec
);
674 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec
*codec
)
676 return purple_media_codec_get_clock_rate((PurpleMediaCodec
*)codec
);
680 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec
*codec
,
681 const gchar
*name
, const gchar
*value
)
683 purple_media_codec_add_optional_parameter((PurpleMediaCodec
*)codec
, name
, value
);
687 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec
*codec
)
689 return purple_media_codec_get_optional_parameters((PurpleMediaCodec
*)codec
);
693 sipe_backend_set_remote_codecs(struct sipe_backend_media
*media
,
694 struct sipe_backend_stream
*stream
,
697 return purple_media_set_remote_codecs(media
->m
,
704 sipe_backend_get_local_codecs(struct sipe_backend_media
*media
,
705 struct sipe_backend_stream
*stream
)
707 GList
*codecs
= purple_media_get_codecs(media
->m
,
710 gboolean is_conference
= (g_strstr_len(stream
->participant
,
711 strlen(stream
->participant
),
712 "app:conf:audio-video:") != NULL
);
715 * Do not announce Theora. Its optional parameters are too long,
716 * Communicator rejects such SDP message and does not support the codec
719 * For some yet unknown reason, A/V conferencing server does not accept
720 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
721 * we are able to decode incoming SIREN from server and with MSOC
722 * client, bidirectional call using the codec works. Until resolved,
723 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
724 * seems to work properly in this scenario.
727 PurpleMediaCodec
*codec
= i
->data
;
728 gchar
*encoding_name
= purple_media_codec_get_encoding_name(codec
);
730 if (sipe_strequal(encoding_name
,"THEORA") ||
731 (is_conference
&& sipe_strequal(encoding_name
,"SIREN"))) {
733 g_object_unref(codec
);
735 codecs
= g_list_delete_link(codecs
, i
);
740 g_free(encoding_name
);
746 struct sipe_backend_candidate
*
747 sipe_backend_candidate_new(const gchar
*foundation
,
748 SipeComponentType component
,
749 SipeCandidateType type
, SipeNetworkProtocol proto
,
750 const gchar
*ip
, guint port
,
751 const gchar
*username
,
752 const gchar
*password
)
754 PurpleMediaCandidate
*c
= purple_media_candidate_new(
755 /* Libnice and Farsight rely on non-NULL foundation to
756 * distinguish between candidates of a component. When NULL
757 * foundation is passed (ie. ICE draft 6 does not use foudation),
758 * use username instead. If no foundation is provided, Farsight
759 * may signal an active candidate different from the one actually
760 * in use. See Farsight's agent_new_selected_pair() in
761 * fs-nice-stream-transmitter.h where first candidate in the
762 * remote list is always selected when no foundation. */
763 foundation
? foundation
: username
,
765 sipe_candidate_type_to_purple(type
),
766 sipe_network_protocol_to_purple(proto
),
769 g_object_set(c
, "username", username
, "password", password
, NULL
);
770 return (struct sipe_backend_candidate
*)c
;
774 sipe_backend_candidate_free(struct sipe_backend_candidate
*candidate
)
777 g_object_unref(candidate
);
781 sipe_backend_candidate_get_username(struct sipe_backend_candidate
*candidate
)
783 /* Not explicitly documented, but return value must be g_free()'d */
784 return purple_media_candidate_get_username((PurpleMediaCandidate
*)candidate
);
788 sipe_backend_candidate_get_password(struct sipe_backend_candidate
*candidate
)
790 /* Not explicitly documented, but return value must be g_free()'d */
791 return purple_media_candidate_get_password((PurpleMediaCandidate
*)candidate
);
795 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate
*candidate
)
797 /* Not explicitly documented, but return value must be g_free()'d */
798 return purple_media_candidate_get_foundation((PurpleMediaCandidate
*)candidate
);
802 sipe_backend_candidate_get_ip(struct sipe_backend_candidate
*candidate
)
804 /* Not explicitly documented, but return value must be g_free()'d */
805 return purple_media_candidate_get_ip((PurpleMediaCandidate
*)candidate
);
809 sipe_backend_candidate_get_port(struct sipe_backend_candidate
*candidate
)
811 return purple_media_candidate_get_port((PurpleMediaCandidate
*)candidate
);
815 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate
*candidate
)
817 /* Not explicitly documented, but return value must be g_free()'d */
818 return purple_media_candidate_get_base_ip((PurpleMediaCandidate
*)candidate
);
822 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate
*candidate
)
824 return purple_media_candidate_get_base_port((PurpleMediaCandidate
*)candidate
);
828 sipe_backend_candidate_get_priority(struct sipe_backend_candidate
*candidate
)
830 return purple_media_candidate_get_priority((PurpleMediaCandidate
*)candidate
);
834 sipe_backend_candidate_set_priority(struct sipe_backend_candidate
*candidate
, guint32 priority
)
836 g_object_set(candidate
, "priority", priority
, NULL
);
840 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate
*candidate
)
842 return purple_media_candidate_get_component_id((PurpleMediaCandidate
*)candidate
);
846 sipe_backend_candidate_get_type(struct sipe_backend_candidate
*candidate
)
848 PurpleMediaCandidateType type
=
849 purple_media_candidate_get_candidate_type((PurpleMediaCandidate
*)candidate
);
850 return purple_candidate_type_to_sipe(type
);
854 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate
*candidate
)
856 PurpleMediaNetworkProtocol proto
=
857 purple_media_candidate_get_protocol((PurpleMediaCandidate
*)candidate
);
858 return purple_network_protocol_to_sipe(proto
);
862 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key
,
866 GList
*entry
= value
;
867 GList
**candidates
= user_data
;
869 g_object_unref(entry
->data
);
870 *candidates
= g_list_delete_link(*candidates
, entry
);
874 ensure_candidate_pairs(GList
*candidates
)
876 GHashTable
*lone_cand_links
;
879 lone_cand_links
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
881 for (i
= candidates
; i
; i
= i
->next
) {
882 PurpleMediaCandidate
*c
= i
->data
;
883 gchar
*foundation
= purple_media_candidate_get_foundation(c
);
885 if (g_hash_table_lookup(lone_cand_links
, foundation
)) {
886 g_hash_table_remove(lone_cand_links
, foundation
);
889 g_hash_table_insert(lone_cand_links
, foundation
, i
);
893 g_hash_table_foreach(lone_cand_links
, remove_lone_candidate_cb
, &candidates
);
894 g_hash_table_destroy(lone_cand_links
);
900 sipe_backend_get_local_candidates(struct sipe_backend_media
*media
,
901 struct sipe_backend_stream
*stream
)
903 GList
*candidates
= purple_media_get_local_candidates(media
->m
,
905 stream
->participant
);
907 * Sometimes purple will not return complete list of candidates, even
908 * after "candidates-prepared" signal is emitted. This is a feature of
909 * libnice, namely affecting candidates discovered via UPnP. Nice does
910 * not wait until discovery is finished and can signal end of candidate
911 * gathering before all responses from UPnP enabled gateways are received.
913 * Remove any incomplete RTP+RTCP candidate pairs from the list.
915 candidates
= ensure_candidate_pairs(candidates
);
920 sipe_backend_media_accept(struct sipe_backend_media
*media
, gboolean local
)
923 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_ACCEPT
,
928 sipe_backend_media_hangup(struct sipe_backend_media
*media
, gboolean local
)
931 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_HANGUP
,
936 sipe_backend_media_reject(struct sipe_backend_media
*media
, gboolean local
)
939 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_REJECT
,
943 static PurpleMediaSessionType
sipe_media_to_purple(SipeMediaType type
)
946 case SIPE_MEDIA_AUDIO
: return PURPLE_MEDIA_AUDIO
;
947 case SIPE_MEDIA_VIDEO
: return PURPLE_MEDIA_VIDEO
;
948 default: return PURPLE_MEDIA_NONE
;
952 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
955 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
956 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
957 default: return SIPE_MEDIA_AUDIO;
961 static PurpleMediaCandidateType
962 sipe_candidate_type_to_purple(SipeCandidateType type
)
965 case SIPE_CANDIDATE_TYPE_HOST
: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
966 case SIPE_CANDIDATE_TYPE_RELAY
: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
;
967 case SIPE_CANDIDATE_TYPE_SRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
;
968 case SIPE_CANDIDATE_TYPE_PRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
;
969 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
973 static SipeCandidateType
974 purple_candidate_type_to_sipe(PurpleMediaCandidateType type
)
977 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST
: return SIPE_CANDIDATE_TYPE_HOST
;
978 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
: return SIPE_CANDIDATE_TYPE_RELAY
;
979 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
: return SIPE_CANDIDATE_TYPE_SRFLX
;
980 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
: return SIPE_CANDIDATE_TYPE_PRFLX
;
981 default: return SIPE_CANDIDATE_TYPE_HOST
;
985 static PurpleMediaNetworkProtocol
986 sipe_network_protocol_to_purple(SipeNetworkProtocol proto
)
990 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
991 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE
;
992 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
:
993 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE
;
995 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
:
996 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
:
997 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
;
999 case SIPE_NETWORK_PROTOCOL_UDP
:
1000 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
;
1002 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
;
1006 static SipeNetworkProtocol
1007 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto
)
1011 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE
:
1012 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
1013 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE
:
1014 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE
;
1016 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
:
1017 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE
;
1019 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
:
1020 return SIPE_NETWORK_PROTOCOL_UDP
;
1022 return SIPE_NETWORK_PROTOCOL_UDP
;