6 * Copyright (C) 2010 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
24 #include "glib/gstdio.h"
29 #include "sipe-common.h"
31 #include "mediamanager.h"
32 #include "media-gst.h"
36 #include "sipe-backend.h"
37 #include "sipe-core.h"
39 #include "purple-private.h"
41 struct sipe_backend_media
{
45 * Number of media streams that were not yet locally accepted or rejected.
47 guint unconfirmed_streams
;
50 struct sipe_backend_stream
{
53 gboolean candidates_prepared
;
54 gboolean local_on_hold
;
55 gboolean remote_on_hold
;
60 backend_stream_free(struct sipe_backend_stream
*stream
)
63 g_free(stream
->sessionid
);
64 g_free(stream
->participant
);
69 static PurpleMediaSessionType
sipe_media_to_purple(SipeMediaType type
);
70 static PurpleMediaCandidateType
sipe_candidate_type_to_purple(SipeCandidateType type
);
71 static SipeCandidateType
purple_candidate_type_to_sipe(PurpleMediaCandidateType type
);
72 static PurpleMediaNetworkProtocol
sipe_network_protocol_to_purple(SipeNetworkProtocol proto
);
73 static SipeNetworkProtocol
purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto
);
76 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
78 SIPE_UNUSED_PARAMETER gchar
*participant
,
79 struct sipe_media_call
*call
)
81 struct sipe_backend_stream
*stream
;
82 stream
= sipe_backend_media_get_stream_by_id(call
->backend_private
, sessionid
);
84 stream
->candidates_prepared
= TRUE
;
86 if (call
->candidates_prepared_cb
&&
87 sipe_backend_candidates_prepared(call
->backend_private
)) {
88 call
->candidates_prepared_cb(call
, stream
);
93 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
94 PurpleMediaState state
,
97 struct sipe_media_call
*call
)
99 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state
, sessionid
, participant
);
100 if (state
== PURPLE_MEDIA_STATE_END
&&
101 !sessionid
&& !participant
&& call
->media_end_cb
)
102 call
->media_end_cb(call
);
105 /* Used externally in purple-plugin.c. This declaration stops the compiler
106 * complaining about missing prototype. */
107 void capture_pipeline(gchar
*label
);
110 capture_pipeline(gchar
*label
) {
111 PurpleMediaManager
*manager
= purple_media_manager_get();
112 GstElement
*pipeline
= purple_media_manager_get_pipeline(manager
);
113 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline
), GST_DEBUG_GRAPH_SHOW_ALL
, label
);
117 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
, gchar
*message
,
118 struct sipe_media_call
*call
)
120 capture_pipeline("ERROR");
123 call
->error_cb(call
, message
);
127 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
128 PurpleMediaInfoType type
,
132 struct sipe_media_call
*call
)
134 if (type
== PURPLE_MEDIA_INFO_ACCEPT
) {
135 if (call
->call_accept_cb
&& !sessionid
&& !participant
)
136 call
->call_accept_cb(call
, local
);
137 else if (sessionid
&& participant
) {
138 struct sipe_backend_stream
*stream
;
139 stream
= sipe_backend_media_get_stream_by_id(call
->backend_private
,
142 if (!stream
->accepted
&& local
)
143 --call
->backend_private
->unconfirmed_streams
;
144 stream
->accepted
= TRUE
;
147 } else if (type
== PURPLE_MEDIA_INFO_HOLD
|| type
== PURPLE_MEDIA_INFO_UNHOLD
) {
149 gboolean state
= (type
== PURPLE_MEDIA_INFO_HOLD
);
152 // Hold specific stream
153 struct sipe_backend_stream
*stream
;
154 stream
= sipe_backend_media_get_stream_by_id(call
->backend_private
,
158 stream
->local_on_hold
= state
;
160 stream
->remote_on_hold
= state
;
163 GSList
*i
= sipe_backend_media_get_streams(call
->backend_private
);
164 for (; i
; i
= i
->next
) {
165 struct sipe_backend_stream
*stream
= i
->data
;
168 stream
->local_on_hold
= state
;
170 stream
->remote_on_hold
= state
;
174 if (call
->call_hold_cb
)
175 call
->call_hold_cb(call
, local
, state
);
176 } else if (type
== PURPLE_MEDIA_INFO_HANGUP
|| type
== PURPLE_MEDIA_INFO_REJECT
) {
177 if (!sessionid
&& !participant
) {
178 if (type
== PURPLE_MEDIA_INFO_HANGUP
&& call
->call_hangup_cb
)
179 call
->call_hangup_cb(call
, local
);
180 else if (type
== PURPLE_MEDIA_INFO_REJECT
&& call
->call_reject_cb
&& !local
)
181 call
->call_reject_cb(call
, local
);
182 } else if (sessionid
&& participant
) {
183 struct sipe_backend_stream
*stream
;
184 stream
= sipe_backend_media_get_stream_by_id(call
->backend_private
,
188 call
->backend_private
->streams
= g_slist_remove(call
->backend_private
->streams
, stream
);
189 backend_stream_free(stream
);
190 if (local
&& --call
->backend_private
->unconfirmed_streams
== 0 &&
191 call
->call_reject_cb
)
192 call
->call_reject_cb(call
, local
);
198 struct sipe_backend_media
*
199 sipe_backend_media_new(struct sipe_core_public
*sipe_public
,
200 struct sipe_media_call
*call
,
201 const gchar
*participant
,
204 struct sipe_backend_media
*media
= g_new0(struct sipe_backend_media
, 1);
205 struct sipe_backend_private
*purple_private
= sipe_public
->backend_private
;
206 PurpleMediaManager
*manager
= purple_media_manager_get();
207 GstElement
*pipeline
;
209 media
->m
= purple_media_manager_create_media(manager
,
210 purple_private
->account
,
212 participant
, initiator
);
214 g_signal_connect(G_OBJECT(media
->m
), "candidates-prepared",
215 G_CALLBACK(on_candidates_prepared_cb
), call
);
216 g_signal_connect(G_OBJECT(media
->m
), "stream-info",
217 G_CALLBACK(on_stream_info_cb
), call
);
218 g_signal_connect(G_OBJECT(media
->m
), "error",
219 G_CALLBACK(on_error_cb
), call
);
220 g_signal_connect(G_OBJECT(media
->m
), "state-changed",
221 G_CALLBACK(on_state_changed_cb
), call
);
223 /* On error, the pipeline is no longer in PLAYING state and libpurple
224 * will not switch it back to PLAYING, preventing any more calls until
225 * application restart. We switch the state ourselves here to negate
226 * effect of any error in previous call (if any). */
227 pipeline
= purple_media_manager_get_pipeline(manager
);
228 gst_element_set_state(pipeline
, GST_STATE_PLAYING
);
234 sipe_backend_media_free(struct sipe_backend_media
*media
)
237 GSList
*stream
= media
->streams
;
239 for (; stream
; stream
= g_slist_delete_link(stream
, stream
))
240 backend_stream_free(stream
->data
);
247 sipe_backend_media_set_cname(struct sipe_backend_media
*media
, gchar
*cname
)
250 guint num_params
= 3;
251 GParameter
*params
= g_new0(GParameter
, num_params
);
252 params
[0].name
= "sdes-cname";
253 g_value_init(¶ms
[0].value
, G_TYPE_STRING
);
254 g_value_set_string(¶ms
[0].value
, cname
);
255 params
[1].name
= "sdes-name";
256 g_value_init(¶ms
[1].value
, G_TYPE_STRING
);
257 params
[2].name
= "sdes-tool";
258 g_value_init(¶ms
[2].value
, G_TYPE_STRING
);
260 purple_media_set_params(media
->m
, num_params
, params
);
262 g_value_unset(¶ms
[0].value
);
267 #define FS_CODECS_CONF \
268 "# Automatically created by SIPE plugin\n" \
270 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
273 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
276 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
282 filename
= g_build_filename(purple_user_dir(), "fs-codec.conf", NULL
);
284 if (!g_file_test(filename
, G_FILE_TEST_EXISTS
)) {
285 int fd
= g_open(filename
, O_CREAT
| O_WRONLY
, S_IRUSR
| S_IWUSR
);
286 gchar
*fs_codecs_conf
= FS_CODECS_CONF
;
287 if (!fd
|| write(fd
, fs_codecs_conf
, strlen(fs_codecs_conf
)) == -1)
288 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
295 struct sipe_backend_media_relays
*
296 sipe_backend_media_relays_convert(GSList
*media_relays
, gchar
*username
, gchar
*password
)
298 GValueArray
*relay_info
= g_value_array_new(0);
300 for (; media_relays
; media_relays
= media_relays
->next
) {
301 struct sipe_media_relay
*relay
= media_relays
->data
;
303 GstStructure
*gst_relay_info
;
305 /* Skip relays where IP could not be resolved. */
306 if (!relay
->hostname
)
309 gst_relay_info
= gst_structure_new("relay-info",
310 "ip", G_TYPE_STRING
, relay
->hostname
,
311 "port", G_TYPE_UINT
, relay
->udp_port
,
312 "username", G_TYPE_STRING
, username
,
313 "password", G_TYPE_STRING
, password
,
316 if (!gst_relay_info
) {
317 g_value_array_free(relay_info
);
321 memset(&value
, 0, sizeof(GValue
));
322 g_value_init(&value
, GST_TYPE_STRUCTURE
);
323 gst_value_set_structure(&value
, gst_relay_info
);
325 relay_info
= g_value_array_append(relay_info
, &value
);
326 gst_structure_free(gst_relay_info
);
329 return (struct sipe_backend_media_relays
*)relay_info
;
333 sipe_backend_media_relays_free(struct sipe_backend_media_relays
*media_relays
)
335 g_value_array_free((GValueArray
*)media_relays
);
338 struct sipe_backend_stream
*
339 sipe_backend_media_add_stream(struct sipe_backend_media
*media
,
341 const gchar
*participant
,
343 SipeIceVersion ice_version
,
345 struct sipe_backend_media_relays
*media_relays
)
347 struct sipe_backend_stream
*stream
= NULL
;
348 PurpleMediaSessionType prpl_type
= sipe_media_to_purple(type
);
349 GParameter
*params
= NULL
;
350 guint params_cnt
= 0;
353 if (ice_version
!= SIPE_ICE_NO_ICE
) {
354 transmitter
= "nice";
357 params
= g_new0(GParameter
, params_cnt
);
359 params
[0].name
= "compatibility-mode";
360 g_value_init(¶ms
[0].value
, G_TYPE_UINT
);
361 g_value_set_uint(¶ms
[0].value
,
362 ice_version
== SIPE_ICE_DRAFT_6
?
363 NICE_COMPATIBILITY_OC2007
:
364 NICE_COMPATIBILITY_OC2007R2
);
367 params
[1].name
= "relay-info";
368 g_value_init(¶ms
[1].value
, G_TYPE_VALUE_ARRAY
);
369 g_value_set_boxed(¶ms
[1].value
, media_relays
);
373 // TODO: session naming here, Communicator needs audio/video
374 transmitter
= "rawudp";
375 //sessionid = "sipe-voice-rawudp";
378 ensure_codecs_conf();
380 if (purple_media_add_stream(media
->m
, id
, participant
, prpl_type
,
381 initiator
, transmitter
, params_cnt
,
383 stream
= g_new0(struct sipe_backend_stream
, 1);
384 stream
->sessionid
= g_strdup(id
);
385 stream
->participant
= g_strdup(participant
);
386 stream
->candidates_prepared
= FALSE
;
388 media
->streams
= g_slist_append(media
->streams
, stream
);
390 ++media
->unconfirmed_streams
;
394 g_value_unset(¶ms
[1].value
);
402 sipe_backend_media_remove_stream(struct sipe_backend_media
*media
,
403 struct sipe_backend_stream
*stream
)
405 g_return_if_fail(media
&& stream
);
407 purple_media_end(media
->m
, stream
->sessionid
, NULL
);
408 media
->streams
= g_slist_remove(media
->streams
, stream
);
409 backend_stream_free(stream
);
412 GSList
*sipe_backend_media_get_streams(struct sipe_backend_media
*media
)
414 return media
->streams
;
417 struct sipe_backend_stream
*
418 sipe_backend_media_get_stream_by_id(struct sipe_backend_media
*media
,
422 for (i
= media
->streams
; i
; i
= i
->next
) {
423 struct sipe_backend_stream
*stream
= i
->data
;
424 if (sipe_strequal(stream
->sessionid
, id
))
431 sipe_backend_media_add_remote_candidates(struct sipe_backend_media
*media
,
432 struct sipe_backend_stream
*stream
,
435 purple_media_add_remote_candidates(media
->m
, stream
->sessionid
,
436 stream
->participant
, candidates
);
439 gboolean
sipe_backend_media_is_initiator(struct sipe_backend_media
*media
,
440 struct sipe_backend_stream
*stream
)
442 return purple_media_is_initiator(media
->m
,
443 stream
? stream
->sessionid
: NULL
,
444 stream
? stream
->participant
: NULL
);
447 gboolean
sipe_backend_media_accepted(struct sipe_backend_media
*media
)
449 return purple_media_accepted(media
->m
, NULL
, NULL
);
453 sipe_backend_candidates_prepared(struct sipe_backend_media
*media
)
455 GSList
*streams
= media
->streams
;
456 for (; streams
; streams
= streams
->next
) {
457 struct sipe_backend_stream
*s
= streams
->data
;
458 if (!s
->candidates_prepared
)
465 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media
*media
,
466 struct sipe_backend_stream
*stream
)
468 return purple_media_get_active_local_candidates(media
->m
,
470 stream
->participant
);
474 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media
*media
,
475 struct sipe_backend_stream
*stream
)
477 return purple_media_get_active_remote_candidates(media
->m
,
479 stream
->participant
);
483 sipe_backend_stream_get_id(struct sipe_backend_stream
*stream
)
485 return stream
->sessionid
;
488 void sipe_backend_stream_hold(struct sipe_backend_media
*media
,
489 struct sipe_backend_stream
*stream
,
492 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_HOLD
,
493 stream
->sessionid
, stream
->participant
,
497 void sipe_backend_stream_unhold(struct sipe_backend_media
*media
,
498 struct sipe_backend_stream
*stream
,
501 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_UNHOLD
,
502 stream
->sessionid
, stream
->participant
,
506 gboolean
sipe_backend_stream_is_held(struct sipe_backend_stream
*stream
)
508 g_return_val_if_fail(stream
, FALSE
);
510 return stream
->local_on_hold
|| stream
->remote_on_hold
;
513 struct sipe_backend_codec
*
514 sipe_backend_codec_new(int id
, const char *name
, SipeMediaType type
, guint clock_rate
)
516 return (struct sipe_backend_codec
*)purple_media_codec_new(id
, name
,
517 sipe_media_to_purple(type
),
522 sipe_backend_codec_free(struct sipe_backend_codec
*codec
)
525 g_object_unref(codec
);
529 sipe_backend_codec_get_id(struct sipe_backend_codec
*codec
)
531 return purple_media_codec_get_id((PurpleMediaCodec
*)codec
);
535 sipe_backend_codec_get_name(struct sipe_backend_codec
*codec
)
537 /* Not explicitly documented, but return value must be g_free()'d */
538 return purple_media_codec_get_encoding_name((PurpleMediaCodec
*)codec
);
542 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec
*codec
)
544 return purple_media_codec_get_clock_rate((PurpleMediaCodec
*)codec
);
548 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec
*codec
,
549 const gchar
*name
, const gchar
*value
)
551 purple_media_codec_add_optional_parameter((PurpleMediaCodec
*)codec
, name
, value
);
555 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec
*codec
)
557 return purple_media_codec_get_optional_parameters((PurpleMediaCodec
*)codec
);
561 sipe_backend_set_remote_codecs(struct sipe_backend_media
*media
,
562 struct sipe_backend_stream
*stream
,
565 return purple_media_set_remote_codecs(media
->m
,
572 sipe_backend_get_local_codecs(struct sipe_backend_media
*media
,
573 struct sipe_backend_stream
*stream
)
575 GList
*codecs
= purple_media_get_codecs(media
->m
,
578 gboolean is_conference
= (g_strstr_len(stream
->participant
,
579 strlen(stream
->participant
),
580 "app:conf:audio-video:") != NULL
);
583 * Do not announce Theora. Its optional parameters are too long,
584 * Communicator rejects such SDP message and does not support the codec
587 * For some yet unknown reason, A/V conferencing server does not accept
588 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
589 * we are able to decode incoming SIREN from server and with MSOC
590 * client, bidirectional call using the codec works. Until resolved,
591 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
592 * seems to work properly in this scenario.
595 PurpleMediaCodec
*codec
= i
->data
;
596 gchar
*encoding_name
= purple_media_codec_get_encoding_name(codec
);
598 if (sipe_strequal(encoding_name
,"THEORA") ||
599 (is_conference
&& sipe_strequal(encoding_name
,"SIREN"))) {
601 g_object_unref(codec
);
603 codecs
= g_list_delete_link(codecs
, i
);
608 g_free(encoding_name
);
614 struct sipe_backend_candidate
*
615 sipe_backend_candidate_new(const gchar
*foundation
,
616 SipeComponentType component
,
617 SipeCandidateType type
, SipeNetworkProtocol proto
,
618 const gchar
*ip
, guint port
,
619 const gchar
*username
,
620 const gchar
*password
)
622 PurpleMediaCandidate
*c
= purple_media_candidate_new(
623 /* Libnice and Farsight rely on non-NULL foundation to
624 * distinguish between candidates of a component. When NULL
625 * foundation is passed (ie. ICE draft 6 does not use foudation),
626 * use username instead. If no foundation is provided, Farsight
627 * may signal an active candidate different from the one actually
628 * in use. See Farsight's agent_new_selected_pair() in
629 * fs-nice-stream-transmitter.h where first candidate in the
630 * remote list is always selected when no foundation. */
631 foundation
? foundation
: username
,
633 sipe_candidate_type_to_purple(type
),
634 sipe_network_protocol_to_purple(proto
),
637 g_object_set(c
, "username", username
, "password", password
, NULL
);
638 return (struct sipe_backend_candidate
*)c
;
642 sipe_backend_candidate_free(struct sipe_backend_candidate
*candidate
)
645 g_object_unref(candidate
);
649 sipe_backend_candidate_get_username(struct sipe_backend_candidate
*candidate
)
651 /* Not explicitly documented, but return value must be g_free()'d */
652 return purple_media_candidate_get_username((PurpleMediaCandidate
*)candidate
);
656 sipe_backend_candidate_get_password(struct sipe_backend_candidate
*candidate
)
658 /* Not explicitly documented, but return value must be g_free()'d */
659 return purple_media_candidate_get_password((PurpleMediaCandidate
*)candidate
);
663 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate
*candidate
)
665 /* Not explicitly documented, but return value must be g_free()'d */
666 return purple_media_candidate_get_foundation((PurpleMediaCandidate
*)candidate
);
670 sipe_backend_candidate_get_ip(struct sipe_backend_candidate
*candidate
)
672 /* Not explicitly documented, but return value must be g_free()'d */
673 return purple_media_candidate_get_ip((PurpleMediaCandidate
*)candidate
);
677 sipe_backend_candidate_get_port(struct sipe_backend_candidate
*candidate
)
679 return purple_media_candidate_get_port((PurpleMediaCandidate
*)candidate
);
683 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate
*candidate
)
685 /* Not explicitly documented, but return value must be g_free()'d */
686 return purple_media_candidate_get_base_ip((PurpleMediaCandidate
*)candidate
);
690 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate
*candidate
)
692 return purple_media_candidate_get_base_port((PurpleMediaCandidate
*)candidate
);
696 sipe_backend_candidate_get_priority(struct sipe_backend_candidate
*candidate
)
698 return purple_media_candidate_get_priority((PurpleMediaCandidate
*)candidate
);
702 sipe_backend_candidate_set_priority(struct sipe_backend_candidate
*candidate
, guint32 priority
)
704 g_object_set(candidate
, "priority", priority
, NULL
);
708 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate
*candidate
)
710 return purple_media_candidate_get_component_id((PurpleMediaCandidate
*)candidate
);
714 sipe_backend_candidate_get_type(struct sipe_backend_candidate
*candidate
)
716 PurpleMediaCandidateType type
=
717 purple_media_candidate_get_candidate_type((PurpleMediaCandidate
*)candidate
);
718 return purple_candidate_type_to_sipe(type
);
722 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate
*candidate
)
724 PurpleMediaNetworkProtocol proto
=
725 purple_media_candidate_get_protocol((PurpleMediaCandidate
*)candidate
);
726 return purple_network_protocol_to_sipe(proto
);
730 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key
,
734 GList
*entry
= value
;
735 GList
**candidates
= user_data
;
737 g_object_unref(entry
->data
);
738 *candidates
= g_list_delete_link(*candidates
, entry
);
742 ensure_candidate_pairs(GList
*candidates
)
744 GHashTable
*lone_cand_links
;
747 lone_cand_links
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
749 for (i
= candidates
; i
; i
= i
->next
) {
750 PurpleMediaCandidate
*c
= i
->data
;
751 gchar
*foundation
= purple_media_candidate_get_foundation(c
);
753 if (g_hash_table_lookup(lone_cand_links
, foundation
)) {
754 g_hash_table_remove(lone_cand_links
, foundation
);
757 g_hash_table_insert(lone_cand_links
, foundation
, i
);
761 g_hash_table_foreach(lone_cand_links
, remove_lone_candidate_cb
, &candidates
);
762 g_hash_table_destroy(lone_cand_links
);
768 sipe_backend_get_local_candidates(struct sipe_backend_media
*media
,
769 struct sipe_backend_stream
*stream
)
771 GList
*candidates
= purple_media_get_local_candidates(media
->m
,
773 stream
->participant
);
775 * Sometimes purple will not return complete list of candidates, even
776 * after "candidates-prepared" signal is emitted. This is a feature of
777 * libnice, namely affecting candidates discovered via UPnP. Nice does
778 * not wait until discovery is finished and can signal end of candidate
779 * gathering before all responses from UPnP enabled gateways are received.
781 * Remove any incomplete RTP+RTCP candidate pairs from the list.
783 candidates
= ensure_candidate_pairs(candidates
);
788 sipe_backend_media_accept(struct sipe_backend_media
*media
, gboolean local
)
791 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_ACCEPT
,
796 sipe_backend_media_hangup(struct sipe_backend_media
*media
, gboolean local
)
799 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_HANGUP
,
804 sipe_backend_media_reject(struct sipe_backend_media
*media
, gboolean local
)
807 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_REJECT
,
811 static PurpleMediaSessionType
sipe_media_to_purple(SipeMediaType type
)
814 case SIPE_MEDIA_AUDIO
: return PURPLE_MEDIA_AUDIO
;
815 case SIPE_MEDIA_VIDEO
: return PURPLE_MEDIA_VIDEO
;
816 default: return PURPLE_MEDIA_NONE
;
820 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
823 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
824 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
825 default: return SIPE_MEDIA_AUDIO;
829 static PurpleMediaCandidateType
830 sipe_candidate_type_to_purple(SipeCandidateType type
)
833 case SIPE_CANDIDATE_TYPE_HOST
: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
834 case SIPE_CANDIDATE_TYPE_RELAY
: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
;
835 case SIPE_CANDIDATE_TYPE_SRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
;
836 case SIPE_CANDIDATE_TYPE_PRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
;
837 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
841 static SipeCandidateType
842 purple_candidate_type_to_sipe(PurpleMediaCandidateType type
)
845 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST
: return SIPE_CANDIDATE_TYPE_HOST
;
846 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
: return SIPE_CANDIDATE_TYPE_RELAY
;
847 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
: return SIPE_CANDIDATE_TYPE_SRFLX
;
848 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
: return SIPE_CANDIDATE_TYPE_PRFLX
;
849 default: return SIPE_CANDIDATE_TYPE_HOST
;
853 static PurpleMediaNetworkProtocol
854 sipe_network_protocol_to_purple(SipeNetworkProtocol proto
)
857 case SIPE_NETWORK_PROTOCOL_TCP
: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
;
858 case SIPE_NETWORK_PROTOCOL_UDP
: return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
;
859 default: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
;
863 static SipeNetworkProtocol
864 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto
)
867 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
: return SIPE_NETWORK_PROTOCOL_TCP
;
868 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
: return SIPE_NETWORK_PROTOCOL_UDP
;
869 default: return SIPE_NETWORK_PROTOCOL_UDP
;