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
{
48 static PurpleMediaSessionType
sipe_media_to_purple(SipeMediaType type
);
49 static PurpleMediaCandidateType
sipe_candidate_type_to_purple(SipeCandidateType type
);
50 static SipeCandidateType
purple_candidate_type_to_sipe(PurpleMediaCandidateType type
);
51 static PurpleMediaNetworkProtocol
sipe_network_protocol_to_purple(SipeNetworkProtocol proto
);
52 static SipeNetworkProtocol
purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto
);
55 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
56 SIPE_UNUSED_PARAMETER gchar
*sessionid
,
57 SIPE_UNUSED_PARAMETER gchar
*participant
,
58 struct sipe_media_call
*call
)
60 if (call
->candidates_prepared_cb
)
61 call
->candidates_prepared_cb(call
);
65 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
66 PurpleMediaState state
,
69 struct sipe_media_call
*call
)
71 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state
, sessionid
, participant
);
72 if (state
== PURPLE_MEDIA_STATE_CONNECTED
&& call
->media_connected_cb
)
73 call
->media_connected_cb(call
);
76 static struct sipe_backend_stream
*
77 session_find(struct sipe_backend_media
*media
,
78 const gchar
*sessionid
,
79 const gchar
*participant
)
81 GSList
*streams
= media
->streams
;
84 struct sipe_backend_stream
*s
= streams
->data
;
85 if ( sipe_strequal(s
->sessionid
, sessionid
)
86 && sipe_strequal(s
->participant
, participant
))
89 streams
= streams
->next
;
96 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia
*media
,
97 PurpleMediaInfoType type
,
101 struct sipe_media_call
*call
)
103 struct sipe_backend_media
*m
= call
->backend_private
;
104 if (m
->in_recursion
) {
105 m
->in_recursion
= FALSE
;
109 if (type
== PURPLE_MEDIA_INFO_ACCEPT
&& call
->call_accept_cb
110 && !sessionid
&& !participant
)
111 call
->call_accept_cb(call
, local
);
112 else if (type
== PURPLE_MEDIA_INFO_HOLD
&& call
->call_hold_cb
) {
113 call
->call_hold_cb(call
, local
, TRUE
);
115 m
->in_recursion
= TRUE
;
116 purple_media_stream_info(m
->m
, PURPLE_MEDIA_INFO_HOLD
, NULL
, NULL
, TRUE
);
118 } else if (type
== PURPLE_MEDIA_INFO_UNHOLD
&& call
->call_hold_cb
) {
119 call
->call_hold_cb(call
, local
, FALSE
);
120 m
->in_recursion
= TRUE
;
121 if (!call
->local_on_hold
&& !call
->remote_on_hold
) {
122 purple_media_stream_info(m
->m
, PURPLE_MEDIA_INFO_UNHOLD
, NULL
, NULL
, TRUE
);
124 /* Remote side is still on hold, keep local also held to prevent sending
125 * unnecessary media over network */
126 purple_media_stream_info(m
->m
, PURPLE_MEDIA_INFO_HOLD
, NULL
, NULL
, TRUE
);
128 } else if (type
== PURPLE_MEDIA_INFO_HANGUP
|| type
== PURPLE_MEDIA_INFO_REJECT
) {
129 if (!sessionid
&& !participant
) {
130 if (type
== PURPLE_MEDIA_INFO_HANGUP
&& call
->call_hangup_cb
)
131 call
->call_hangup_cb(call
, local
);
132 else if (type
== PURPLE_MEDIA_INFO_REJECT
&& call
->call_reject_cb
)
133 call
->call_reject_cb(call
, local
);
134 } else if (sessionid
&& participant
) {
135 struct sipe_backend_stream
*stream
;
136 stream
= session_find(m
, sessionid
, participant
);
139 m
->streams
= g_slist_remove(m
->streams
, stream
);
140 g_free(stream
->participant
);
147 struct sipe_backend_media
*
148 sipe_backend_media_new(struct sipe_core_public
*sipe_public
,
149 struct sipe_media_call
*call
,
150 const gchar
*participant
,
153 struct sipe_backend_media
*media
= g_new0(struct sipe_backend_media
, 1);
154 struct sipe_backend_private
*purple_private
= sipe_public
->backend_private
;
155 PurpleMediaManager
*manager
= purple_media_manager_get();
157 media
->m
= purple_media_manager_create_media(manager
,
158 purple_private
->account
,
160 participant
, initiator
);
162 g_signal_connect(G_OBJECT(media
->m
), "candidates-prepared",
163 G_CALLBACK(on_candidates_prepared_cb
), call
);
164 g_signal_connect(G_OBJECT(media
->m
), "stream-info",
165 G_CALLBACK(on_stream_info_cb
), call
);
166 g_signal_connect(G_OBJECT(media
->m
), "state-changed",
167 G_CALLBACK(on_state_changed_cb
), call
);
173 sipe_backend_media_free(struct sipe_backend_media
*media
)
175 purple_media_manager_remove_media(purple_media_manager_get(), media
->m
);
179 struct sipe_backend_stream
*
180 sipe_backend_media_add_stream(struct sipe_backend_media
*media
,
181 const gchar
* participant
,
186 struct sipe_backend_stream
*stream
= NULL
;
187 PurpleMediaSessionType prpl_type
= sipe_media_to_purple(type
);
188 GParameter
*params
= NULL
;
189 guint params_cnt
= 0;
194 transmitter
= "nice";
197 params
= g_new0(GParameter
, params_cnt
);
198 params
[0].name
= "controlling-mode";
199 g_value_init(¶ms
[0].value
, G_TYPE_BOOLEAN
);
200 g_value_set_boolean(¶ms
[0].value
, initiator
);
201 params
[1].name
= "compatibility-mode";
202 g_value_init(¶ms
[1].value
, G_TYPE_UINT
);
203 g_value_set_uint(¶ms
[1].value
, NICE_COMPATIBILITY_OC2007R2
);
205 sessionid
= "sipe-voice-nice";
207 transmitter
= "rawudp";
208 sessionid
= "sipe-voice-rawudp";
211 if (purple_media_add_stream(media
->m
, sessionid
, participant
, prpl_type
,
212 initiator
, transmitter
, params_cnt
, params
)) {
213 stream
= g_new0(struct sipe_backend_stream
, 1);
214 stream
->sessionid
= sessionid
;
215 stream
->participant
= g_strdup(participant
);
217 media
->streams
= g_slist_append(media
->streams
, stream
);
224 sipe_backend_media_remove_stream(struct sipe_backend_media
*media
, struct sipe_backend_stream
*stream
)
226 purple_media_end(media
->m
, stream
->sessionid
, stream
->participant
);
230 sipe_backend_media_add_remote_candidates(struct sipe_backend_media
*media
,
231 struct sipe_backend_stream
*stream
,
234 purple_media_add_remote_candidates(media
->m
, stream
->sessionid
,
235 stream
->participant
, candidates
);
238 gboolean
sipe_backend_media_is_initiator(struct sipe_backend_media
*media
,
239 struct sipe_backend_stream
*stream
)
241 return purple_media_is_initiator(media
->m
,
243 stream
->participant
);
247 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media
*media
,
248 struct sipe_backend_stream
*stream
)
250 return purple_media_get_active_local_candidates(media
->m
,
252 stream
->participant
);
256 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media
*media
,
257 struct sipe_backend_stream
*stream
)
259 return purple_media_get_active_remote_candidates(media
->m
,
261 stream
->participant
);
264 struct sipe_backend_codec
*
265 sipe_backend_codec_new(int id
, const char *name
, SipeMediaType type
, guint clock_rate
)
267 return (struct sipe_backend_codec
*)purple_media_codec_new(id
, name
,
268 sipe_media_to_purple(type
),
273 sipe_backend_codec_free(struct sipe_backend_codec
*codec
)
276 g_object_unref(codec
);
280 sipe_backend_codec_get_id(struct sipe_backend_codec
*codec
)
282 return purple_media_codec_get_id((PurpleMediaCodec
*)codec
);
286 sipe_backend_codec_get_name(struct sipe_backend_codec
*codec
)
288 /* Not explicitly documented, but return value must be g_free()'d */
289 return purple_media_codec_get_encoding_name((PurpleMediaCodec
*)codec
);
293 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec
*codec
)
295 return purple_media_codec_get_clock_rate((PurpleMediaCodec
*)codec
);
299 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec
*codec
,
300 const gchar
*name
, const gchar
*value
)
302 purple_media_codec_add_optional_parameter((PurpleMediaCodec
*)codec
, name
, value
);
306 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec
*codec
)
308 return purple_media_codec_get_optional_parameters((PurpleMediaCodec
*)codec
);
312 sipe_backend_set_remote_codecs(struct sipe_media_call
*call
,
313 struct sipe_backend_stream
*stream
)
315 return purple_media_set_remote_codecs(call
->backend_private
->m
,
318 call
->remote_codecs
);
322 sipe_backend_get_local_codecs(struct sipe_media_call
*call
,
323 struct sipe_backend_stream
*stream
)
325 return purple_media_get_codecs(call
->backend_private
->m
, stream
->sessionid
);
328 struct sipe_backend_candidate
*
329 sipe_backend_candidate_new(const gchar
*foundation
,
330 SipeComponentType component
,
331 SipeCandidateType type
, SipeNetworkProtocol proto
,
332 const gchar
*ip
, guint port
)
334 return (struct sipe_backend_candidate
*)purple_media_candidate_new(
337 sipe_candidate_type_to_purple(type
),
338 sipe_network_protocol_to_purple(proto
),
344 sipe_backend_candidate_free(struct sipe_backend_candidate
*candidate
)
347 g_object_unref(candidate
);
351 sipe_backend_candidate_get_username(struct sipe_backend_candidate
*candidate
)
353 /* Not explicitly documented, but return value must be g_free()'d */
354 return purple_media_candidate_get_username((PurpleMediaCandidate
*)candidate
);
358 sipe_backend_candidate_get_password(struct sipe_backend_candidate
*candidate
)
360 /* Not explicitly documented, but return value must be g_free()'d */
361 return purple_media_candidate_get_password((PurpleMediaCandidate
*)candidate
);
365 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate
*candidate
)
367 /* Not explicitly documented, but return value must be g_free()'d */
368 return purple_media_candidate_get_foundation((PurpleMediaCandidate
*)candidate
);
372 sipe_backend_candidate_get_ip(struct sipe_backend_candidate
*candidate
)
374 /* Not explicitly documented, but return value must be g_free()'d */
375 return purple_media_candidate_get_ip((PurpleMediaCandidate
*)candidate
);
379 sipe_backend_candidate_get_port(struct sipe_backend_candidate
*candidate
)
381 return purple_media_candidate_get_port((PurpleMediaCandidate
*)candidate
);
385 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate
*candidate
)
387 /* Not explicitly documented, but return value must be g_free()'d */
388 return purple_media_candidate_get_base_ip((PurpleMediaCandidate
*)candidate
);
392 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate
*candidate
)
394 return purple_media_candidate_get_base_port((PurpleMediaCandidate
*)candidate
);
398 sipe_backend_candidate_get_priority(struct sipe_backend_candidate
*candidate
)
400 return purple_media_candidate_get_priority((PurpleMediaCandidate
*)candidate
);
404 sipe_backend_candidate_set_priority(struct sipe_backend_candidate
*candidate
, guint32 priority
)
406 g_object_set(candidate
, "priority", priority
, NULL
);
410 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate
*candidate
)
412 return purple_media_candidate_get_component_id((PurpleMediaCandidate
*)candidate
);
416 sipe_backend_candidate_get_type(struct sipe_backend_candidate
*candidate
)
418 PurpleMediaCandidateType type
=
419 purple_media_candidate_get_candidate_type((PurpleMediaCandidate
*)candidate
);
420 return purple_candidate_type_to_sipe(type
);
424 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate
*candidate
)
426 PurpleMediaNetworkProtocol proto
=
427 purple_media_candidate_get_protocol((PurpleMediaCandidate
*)candidate
);
428 return purple_network_protocol_to_sipe(proto
);
432 sipe_backend_candidate_set_username_and_pwd(struct sipe_backend_candidate
*candidate
,
433 const gchar
*username
,
434 const gchar
*password
)
436 g_object_set(candidate
, "username", username
, "password", password
, NULL
);
440 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key
,
444 GList
*entry
= value
;
445 GList
**candidates
= user_data
;
447 g_object_unref(entry
->data
);
448 *candidates
= g_list_delete_link(*candidates
, entry
);
452 ensure_candidate_pairs(GList
*candidates
)
454 GHashTable
*lone_cand_links
;
457 lone_cand_links
= g_hash_table_new_full(g_str_hash
, g_str_equal
, g_free
, NULL
);
459 for (i
= candidates
; i
; i
= i
->next
) {
460 PurpleMediaCandidate
*c
= i
->data
;
461 gchar
*foundation
= purple_media_candidate_get_foundation(c
);
463 if (g_hash_table_lookup(lone_cand_links
, foundation
)) {
464 g_hash_table_remove(lone_cand_links
, foundation
);
467 g_hash_table_insert(lone_cand_links
, foundation
, i
);
471 g_hash_table_foreach(lone_cand_links
, remove_lone_candidate_cb
, &candidates
);
472 g_hash_table_destroy(lone_cand_links
);
478 sipe_backend_get_local_candidates(struct sipe_backend_media
*media
,
479 struct sipe_backend_stream
*stream
)
481 GList
*candidates
= purple_media_get_local_candidates(media
->m
,
483 stream
->participant
);
485 * Sometimes purple will not return complete list of candidates, even
486 * after "candidates-prepared" signal is emitted. This is a feature of
487 * libnice, namely affecting candidates discovered via UPnP. Nice does
488 * not wait until discovery is finished and can signal end of candidate
489 * gathering before all responses from UPnP enabled gateways are received.
491 * Remove any incomplete RTP+RTCP candidate pairs from the list.
493 candidates
= ensure_candidate_pairs(candidates
);
498 sipe_backend_media_hold(struct sipe_backend_media
*media
, gboolean local
)
500 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_HOLD
,
505 sipe_backend_media_unhold(struct sipe_backend_media
*media
, gboolean local
)
507 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_UNHOLD
,
512 sipe_backend_media_hangup(struct sipe_backend_media
*media
, gboolean local
)
514 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_HANGUP
,
519 sipe_backend_media_reject(struct sipe_backend_media
*media
, gboolean local
)
521 purple_media_stream_info(media
->m
, PURPLE_MEDIA_INFO_REJECT
,
525 static PurpleMediaSessionType
sipe_media_to_purple(SipeMediaType type
)
528 case SIPE_MEDIA_AUDIO
: return PURPLE_MEDIA_AUDIO
;
529 case SIPE_MEDIA_VIDEO
: return PURPLE_MEDIA_VIDEO
;
530 default: return PURPLE_MEDIA_NONE
;
534 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
537 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
538 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
539 default: return SIPE_MEDIA_AUDIO;
543 static PurpleMediaCandidateType
544 sipe_candidate_type_to_purple(SipeCandidateType type
)
547 case SIPE_CANDIDATE_TYPE_HOST
: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
548 case SIPE_CANDIDATE_TYPE_RELAY
: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
;
549 case SIPE_CANDIDATE_TYPE_SRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
;
550 case SIPE_CANDIDATE_TYPE_PRFLX
: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
;
551 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST
;
555 static SipeCandidateType
556 purple_candidate_type_to_sipe(PurpleMediaCandidateType type
)
559 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST
: return SIPE_CANDIDATE_TYPE_HOST
;
560 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY
: return SIPE_CANDIDATE_TYPE_RELAY
;
561 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX
: return SIPE_CANDIDATE_TYPE_SRFLX
;
562 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX
: return SIPE_CANDIDATE_TYPE_PRFLX
;
563 default: return SIPE_CANDIDATE_TYPE_HOST
;
567 static PurpleMediaNetworkProtocol
568 sipe_network_protocol_to_purple(SipeNetworkProtocol proto
)
571 case SIPE_NETWORK_PROTOCOL_TCP
: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
;
572 case SIPE_NETWORK_PROTOCOL_UDP
: return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
;
573 default: return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
;
577 static SipeNetworkProtocol
578 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto
)
581 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP
: return SIPE_NETWORK_PROTOCOL_TCP
;
582 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP
: return SIPE_NETWORK_PROTOCOL_UDP
;
583 default: return SIPE_NETWORK_PROTOCOL_UDP
;