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
25 #include "sipe-common.h"
27 #include "mediamanager.h"
31 #include "sipe-backend.h"
32 #include "sipe-core.h"
34 #include "purple-private.h"
36 struct sipe_backend_media
{
38 // Prevent infinite recursion in on_stream_info_cb
39 gboolean in_recursion
;
43 struct sipe_backend_stream
{
49 backend_stream_free(struct sipe_backend_stream
*stream
)
52 g_free(stream
->participant
);
57 static PurpleMediaSessionType
sipe_media_to_purple(SipeMediaType type
);
58 static PurpleMediaCandidateType
sipe_candidate_type_to_purple(SipeCandidateType type
);
59 static SipeCandidateType
purple_candidate_type_to_sipe(PurpleMediaCandidateType type
);
60 static PurpleMediaNetworkProtocol
sipe_network_protocol_to_purple(SipeNetworkProtocol proto
);
61 static SipeNetworkProtocol
purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto
);
64 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
65 SIPE_UNUSED_PARAMETER gchar
*sessionid
,
66 SIPE_UNUSED_PARAMETER gchar
*participant
,
67 struct sipe_media_call
*call
)
69 if (call
->candidates_prepared_cb
)
70 call
->candidates_prepared_cb(call
);
74 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
75 PurpleMediaState state
,
78 struct sipe_media_call
*call
)
80 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state
, sessionid
, participant
);
81 if (state
== PURPLE_MEDIA_STATE_CONNECTED
&& call
->media_connected_cb
)
82 call
->media_connected_cb(call
);
85 static struct sipe_backend_stream
*
86 stream_find(struct sipe_backend_media
*media
,
87 const gchar
*sessionid
,
88 const gchar
*participant
)
90 GSList
*streams
= media
->streams
;
93 struct sipe_backend_stream
*s
= streams
->data
;
94 if ( sipe_strequal(s
->sessionid
, sessionid
)
95 && sipe_strequal(s
->participant
, participant
))
98 streams
= streams
->next
;
105 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
106 PurpleMediaInfoType type
,
110 struct sipe_media_call
*call
)
112 struct sipe_backend_media
*m
= call
->backend_private
;
113 if (m
->in_recursion
) {
114 m
->in_recursion
= FALSE
;
118 if (type
== PURPLE_MEDIA_INFO_ACCEPT
&& call
->call_accept_cb
119 && !sessionid
&& !participant
)
120 call
->call_accept_cb(call
, local
);
121 else if (type
== PURPLE_MEDIA_INFO_HOLD
&& call
->call_hold_cb
) {
122 call
->call_hold_cb(call
, local
, TRUE
);
124 m
->in_recursion
= TRUE
;
125 purple_media_stream_info(m
->m
, PURPLE_MEDIA_INFO_HOLD
, NULL
, NULL
, TRUE
);
127 } else if (type
== PURPLE_MEDIA_INFO_UNHOLD
&& call
->call_hold_cb
) {
128 call
->call_hold_cb(call
, local
, FALSE
);
129 m
->in_recursion
= TRUE
;
130 if (!call
->local_on_hold
&& !call
->remote_on_hold
) {
131 purple_media_stream_info(m
->m
, PURPLE_MEDIA_INFO_UNHOLD
, NULL
, NULL
, TRUE
);
133 /* Remote side is still on hold, keep local also held to prevent sending
134 * unnecessary media over network */
135 purple_media_stream_info(m
->m
, PURPLE_MEDIA_INFO_HOLD
, NULL
, NULL
, TRUE
);
137 } else if (type
== PURPLE_MEDIA_INFO_HANGUP
|| type
== PURPLE_MEDIA_INFO_REJECT
) {
138 if (!sessionid
&& !participant
) {
139 if (type
== PURPLE_MEDIA_INFO_HANGUP
&& call
->call_hangup_cb
)
140 call
->call_hangup_cb(call
, local
);
141 else if (type
== PURPLE_MEDIA_INFO_REJECT
&& call
->call_reject_cb
)
142 call
->call_reject_cb(call
, local
);
143 } else if (sessionid
&& participant
) {
144 struct sipe_backend_stream
*stream
;
145 stream
= stream_find(m
, sessionid
, participant
);
147 m
->streams
= g_slist_remove(m
->streams
, stream
);
148 backend_stream_free(stream
);
154 struct sipe_backend_media
*
155 sipe_backend_media_new(struct sipe_core_public
*sipe_public
,
156 struct sipe_media_call
*call
,
157 const gchar
*participant
,
160 struct sipe_backend_media
*media
= g_new0(struct sipe_backend_media
, 1);
161 struct sipe_backend_private
*purple_private
= sipe_public
->backend_private
;
162 PurpleMediaManager
*manager
= purple_media_manager_get();
164 media
->m
= purple_media_manager_create_media(manager
,
165 purple_private
->account
,
167 participant
, initiator
);
169 g_signal_connect(G_OBJECT(media
->m
), "candidates-prepared",
170 G_CALLBACK(on_candidates_prepared_cb
), call
);
171 g_signal_connect(G_OBJECT(media
->m
), "stream-info",
172 G_CALLBACK(on_stream_info_cb
), call
);
173 g_signal_connect(G_OBJECT(media
->m
), "state-changed",
174 G_CALLBACK(on_state_changed_cb
), call
);
180 sipe_backend_media_free(struct sipe_backend_media
*media
)
182 GSList
*stream
= media
->streams
;
183 g_object_unref(media
->m
);
185 for (; stream
; stream
= g_slist_delete_link(stream
, stream
))
186 backend_stream_free(stream
->data
);
191 struct sipe_backend_stream
*
192 sipe_backend_media_add_stream(struct sipe_backend_media
*media
,
193 const gchar
* participant
,
198 struct sipe_backend_stream
*stream
= NULL
;
199 PurpleMediaSessionType prpl_type
= sipe_media_to_purple(type
);
200 GParameter
*params
= NULL
;
201 guint params_cnt
= 0;
206 transmitter
= "nice";
209 params
= g_new0(GParameter
, params_cnt
);
210 params
[0].name
= "controlling-mode";
211 g_value_init(¶ms
[0].value
, G_TYPE_BOOLEAN
);
212 g_value_set_boolean(¶ms
[0].value
, initiator
);
213 params
[1].name
= "compatibility-mode";
214 g_value_init(¶ms
[1].value
, G_TYPE_UINT
);
215 g_value_set_uint(¶ms
[1].value
, NICE_COMPATIBILITY_OC2007R2
);
217 sessionid
= "sipe-voice-nice";
219 transmitter
= "rawudp";
220 sessionid
= "sipe-voice-rawudp";
223 if (purple_media_add_stream(media
->m
, sessionid
, participant
, prpl_type
,
224 initiator
, transmitter
, params_cnt
, params
)) {
225 stream
= g_new0(struct sipe_backend_stream
, 1);
226 stream
->sessionid
= sessionid
;
227 stream
->participant
= g_strdup(participant
);
229 media
->streams
= g_slist_append(media
->streams
, stream
);
238 sipe_backend_media_remove_stream(struct sipe_backend_media
*media
, struct sipe_backend_stream
*stream
)
240 purple_media_end(media
->m
, stream
->sessionid
, stream
->participant
);
244 sipe_backend_media_add_remote_candidates(struct sipe_backend_media
*media
,
245 struct sipe_backend_stream
*stream
,
248 purple_media_add_remote_candidates(media
->m
, stream
->sessionid
,
249 stream
->participant
, candidates
);
252 gboolean
sipe_backend_media_is_initiator(struct sipe_backend_media
*media
,
253 struct sipe_backend_stream
*stream
)
255 return purple_media_is_initiator(media
->m
,
256 stream
? stream
->sessionid
: NULL
,
257 stream
? stream
->participant
: NULL
);
260 gboolean
sipe_backend_media_accepted(struct sipe_backend_media
*media
)
262 return purple_media_accepted(media
->m
, NULL
, NULL
);
266 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media
*media
,
267 struct sipe_backend_stream
*stream
)
269 return purple_media_get_active_local_candidates(media
->m
,
271 stream
->participant
);
275 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media
*media
,
276 struct sipe_backend_stream
*stream
)
278 return purple_media_get_active_remote_candidates(media
->m
,
280 stream
->participant
);
283 struct sipe_backend_codec
*
284 sipe_backend_codec_new(int id
, const char *name
, SipeMediaType type
, guint clock_rate
)
286 return (struct sipe_backend_codec
*)purple_media_codec_new(id
, name
,
287 sipe_media_to_purple(type
),
292 sipe_backend_codec_free(struct sipe_backend_codec
*codec
)
295 g_object_unref(codec
);
299 sipe_backend_codec_get_id(struct sipe_backend_codec
*codec
)
301 return purple_media_codec_get_id((PurpleMediaCodec
*)codec
);
305 sipe_backend_codec_get_name(struct sipe_backend_codec
*codec
)
307 /* Not explicitly documented, but return value must be g_free()'d */
308 return purple_media_codec_get_encoding_name((PurpleMediaCodec
*)codec
);
312 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec
*codec
)
314 return purple_media_codec_get_clock_rate((PurpleMediaCodec
*)codec
);
318 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec
*codec
,
319 const gchar
*name
, const gchar
*value
)
321 purple_media_codec_add_optional_parameter((PurpleMediaCodec
*)codec
, name
, value
);
325 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec
*codec
)
327 return purple_media_codec_get_optional_parameters((PurpleMediaCodec
*)codec
);
331 sipe_backend_set_remote_codecs(struct sipe_backend_media
*media
,
332 struct sipe_backend_stream
*stream
,
335 return purple_media_set_remote_codecs(media
->m
,
342 sipe_backend_get_local_codecs(struct sipe_media_call
*call
,
343 struct sipe_backend_stream
*stream
)
345 return purple_media_get_codecs(call
->backend_private
->m
, stream
->sessionid
);
348 struct sipe_backend_candidate
*
349 sipe_backend_candidate_new(const gchar
*foundation
,
350 SipeComponentType component
,
351 SipeCandidateType type
, SipeNetworkProtocol proto
,
352 const gchar
*ip
, guint port
)
354 return (struct sipe_backend_candidate
*)purple_media_candidate_new(
357 sipe_candidate_type_to_purple(type
),
358 sipe_network_protocol_to_purple(proto
),
364 sipe_backend_candidate_free(struct sipe_backend_candidate
*candidate
)
367 g_object_unref(candidate
);
371 sipe_backend_candidate_get_username(struct sipe_backend_candidate
*candidate
)
373 /* Not explicitly documented, but return value must be g_free()'d */
374 return purple_media_candidate_get_username((PurpleMediaCandidate
*)candidate
);
378 sipe_backend_candidate_get_password(struct sipe_backend_candidate
*candidate
)
380 /* Not explicitly documented, but return value must be g_free()'d */
381 return purple_media_candidate_get_password((PurpleMediaCandidate
*)candidate
);
385 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate
*candidate
)
387 /* Not explicitly documented, but return value must be g_free()'d */
388 return purple_media_candidate_get_foundation((PurpleMediaCandidate
*)candidate
);
392 sipe_backend_candidate_get_ip(struct sipe_backend_candidate
*candidate
)
394 /* Not explicitly documented, but return value must be g_free()'d */
395 return purple_media_candidate_get_ip((PurpleMediaCandidate
*)candidate
);
399 sipe_backend_candidate_get_port(struct sipe_backend_candidate
*candidate
)
401 return purple_media_candidate_get_port((PurpleMediaCandidate
*)candidate
);
405 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate
*candidate
)
407 /* Not explicitly documented, but return value must be g_free()'d */
408 return purple_media_candidate_get_base_ip((PurpleMediaCandidate
*)candidate
);
412 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate
*candidate
)
414 return purple_media_candidate_get_base_port((PurpleMediaCandidate
*)candidate
);
418 sipe_backend_candidate_get_priority(struct sipe_backend_candidate
*candidate
)
420 return purple_media_candidate_get_priority((PurpleMediaCandidate
*)candidate
);
424 sipe_backend_candidate_set_priority(struct sipe_backend_candidate
*candidate
, guint32 priority
)
426 g_object_set(candidate
, "priority", priority
, NULL
);
430 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate
*candidate
)
432 return purple_media_candidate_get_component_id((PurpleMediaCandidate
*)candidate
);
436 sipe_backend_candidate_get_type(struct sipe_backend_candidate
*candidate
)
438 PurpleMediaCandidateType type
=
439 purple_media_candidate_get_candidate_type((PurpleMediaCandidate
*)candidate
);
440 return purple_candidate_type_to_sipe(type
);
444 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate
*candidate
)
446 PurpleMediaNetworkProtocol proto
=
447 purple_media_candidate_get_protocol((PurpleMediaCandidate
*)candidate
);
448 return purple_network_protocol_to_sipe(proto
);
452 sipe_backend_candidate_set_username_and_pwd(struct sipe_backend_candidate
*candidate
,
453 const gchar
*username
,
454 const gchar
*password
)
456 g_object_set(candidate
, "username", username
, "password", password
, NULL
);
460 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key
,
464 GList
*entry
= value
;
465 GList
**candidates
= user_data
;
467 g_object_unref(entry
->data
);
468 *candidates
= g_list_delete_link(*candidates
, entry
);
472 ensure_candidate_pairs(GList
*candidates
)
474 GHashTable
*lone_cand_links
;
477 lone_cand_links
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
479 for (i
= candidates
; i
; i
= i
->next
) {
480 PurpleMediaCandidate
*c
= i
->data
;
481 gchar
*foundation
= purple_media_candidate_get_foundation(c
);
483 if (g_hash_table_lookup(lone_cand_links
, foundation
)) {
484 g_hash_table_remove(lone_cand_links
, foundation
);
487 g_hash_table_insert(lone_cand_links
, foundation
, i
);
491 g_hash_table_foreach(lone_cand_links
, remove_lone_candidate_cb
, &candidates
);
492 g_hash_table_destroy(lone_cand_links
);
498 sipe_backend_get_local_candidates(struct sipe_backend_media
*media
,
499 struct sipe_backend_stream
*stream
)
501 GList
*candidates
= purple_media_get_local_candidates(media
->m
,
503 stream
->participant
);
505 * Sometimes purple will not return complete list of candidates, even
506 * after "candidates-prepared" signal is emitted. This is a feature of
507 * libnice, namely affecting candidates discovered via UPnP. Nice does
508 * not wait until discovery is finished and can signal end of candidate
509 * gathering before all responses from UPnP enabled gateways are received.
511 * Remove any incomplete RTP+RTCP candidate pairs from the list.
513 candidates
= ensure_candidate_pairs(candidates
);
518 sipe_backend_media_hold(struct sipe_backend_media
*media
, gboolean local
)
520 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_HOLD
,
525 sipe_backend_media_unhold(struct sipe_backend_media
*media
, gboolean local
)
527 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_UNHOLD
,
532 sipe_backend_media_hangup(struct sipe_backend_media
*media
, gboolean local
)
534 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_HANGUP
,
539 sipe_backend_media_reject(struct sipe_backend_media
*media
, gboolean local
)
541 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_REJECT
,
545 static PurpleMediaSessionType
sipe_media_to_purple(SipeMediaType type
)
548 case SIPE_MEDIA_AUDIO
: return PURPLE_MEDIA_AUDIO
;
549 case SIPE_MEDIA_VIDEO
: return PURPLE_MEDIA_VIDEO
;
550 default: return PURPLE_MEDIA_NONE
;
554 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
557 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
558 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
559 default: return SIPE_MEDIA_AUDIO;
563 static PurpleMediaCandidateType
564 sipe_candidate_type_to_purple(SipeCandidateType type
)
567 case SIPE_CANDIDATE_TYPE_HOST
: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
568 case SIPE_CANDIDATE_TYPE_RELAY
: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
;
569 case SIPE_CANDIDATE_TYPE_SRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
;
570 case SIPE_CANDIDATE_TYPE_PRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
;
571 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
575 static SipeCandidateType
576 purple_candidate_type_to_sipe(PurpleMediaCandidateType type
)
579 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST
: return SIPE_CANDIDATE_TYPE_HOST
;
580 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
: return SIPE_CANDIDATE_TYPE_RELAY
;
581 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
: return SIPE_CANDIDATE_TYPE_SRFLX
;
582 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
: return SIPE_CANDIDATE_TYPE_PRFLX
;
583 default: return SIPE_CANDIDATE_TYPE_HOST
;
587 static PurpleMediaNetworkProtocol
588 sipe_network_protocol_to_purple(SipeNetworkProtocol proto
)
591 case SIPE_NETWORK_PROTOCOL_TCP
: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
;
592 case SIPE_NETWORK_PROTOCOL_UDP
: return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
;
593 default: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
;
597 static SipeNetworkProtocol
598 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto
)
601 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
: return SIPE_NETWORK_PROTOCOL_TCP
;
602 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
: return SIPE_NETWORK_PROTOCOL_UDP
;
603 default: return SIPE_NETWORK_PROTOCOL_UDP
;