audio: don't use GHashTableIter
[siplcs.git] / src / purple / purple-media.c
blobd6828e4cb602d9b59efd82cb56d674564edfb662
1 /**
2 * @file purple-media.c
4 * pidgin-sipe
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
23 #include "glib.h"
25 #include "sipe-common.h"
27 #include "mediamanager.h"
28 #include "request.h"
29 #include "agent.h"
31 #include "sipe-backend.h"
32 #include "sipe-core.h"
34 #include "purple-private.h"
36 struct sipe_backend_media {
37 PurpleMedia *m;
38 // Prevent infinite recursion in on_stream_info_cb
39 gboolean in_recursion;
40 GSList *streams;
43 struct sipe_backend_stream {
44 gchar *sessionid;
45 gchar *participant;
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);
54 static void
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);
64 static void
65 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
66 PurpleMediaState state,
67 gchar *sessionid,
68 gchar *participant,
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;
83 while (streams) {
84 struct sipe_backend_stream *s = streams->data;
85 if ( sipe_strequal(s->sessionid, sessionid)
86 && sipe_strequal(s->participant, participant))
87 return s;
89 streams = streams->next;
92 return NULL;
95 static void
96 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
97 PurpleMediaInfoType type,
98 gchar *sessionid,
99 gchar *participant,
100 gboolean local,
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;
106 return;
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);
114 if (!local) {
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);
123 } else {
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);
138 if (stream) {
139 m->streams = g_slist_remove(m->streams, stream);
140 g_free(stream->participant);
141 g_free(stream);
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,
151 gboolean initiator)
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,
159 "fsrtpconference",
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);
169 return media;
172 void
173 sipe_backend_media_free(struct sipe_backend_media *media)
175 purple_media_manager_remove_media(purple_media_manager_get(), media->m);
176 g_free(media);
179 struct sipe_backend_stream *
180 sipe_backend_media_add_stream(struct sipe_backend_media *media,
181 const gchar* participant,
182 SipeMediaType type,
183 gboolean use_nice,
184 gboolean initiator)
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;
190 gchar *transmitter;
191 gchar *sessionid;
193 if (use_nice) {
194 transmitter = "nice";
195 params_cnt = 2;
197 params = g_new0(GParameter, params_cnt);
198 params[0].name = "controlling-mode";
199 g_value_init(&params[0].value, G_TYPE_BOOLEAN);
200 g_value_set_boolean(&params[0].value, initiator);
201 params[1].name = "compatibility-mode";
202 g_value_init(&params[1].value, G_TYPE_UINT);
203 g_value_set_uint(&params[1].value, NICE_COMPATIBILITY_OC2007R2);
205 sessionid = "sipe-voice-nice";
206 } else {
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);
220 return stream;
223 void
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);
229 void
230 sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
231 struct sipe_backend_stream *stream,
232 GList *candidates)
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,
242 stream->sessionid,
243 stream->participant);
246 GList *
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,
251 stream->sessionid,
252 stream->participant);
255 GList *
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,
260 stream->sessionid,
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),
269 clock_rate);
272 void
273 sipe_backend_codec_free(struct sipe_backend_codec *codec)
275 if (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);
285 gchar *
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);
292 guint
293 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
295 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
298 void
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);
305 GList *
306 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
308 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
311 gboolean
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,
316 stream->sessionid,
317 stream->participant,
318 call->remote_codecs);
321 GList*
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(
335 foundation,
336 component,
337 sipe_candidate_type_to_purple(type),
338 sipe_network_protocol_to_purple(proto),
340 port);
343 void
344 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
346 if (candidate)
347 g_object_unref(candidate);
350 gchar *
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);
357 gchar *
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);
364 gchar *
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);
371 gchar *
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);
378 guint
379 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
381 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
384 gchar *
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);
391 guint
392 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
394 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
397 guint32
398 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
400 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
403 void
404 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
406 g_object_set(candidate, "priority", priority, NULL);
409 SipeComponentType
410 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
412 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
415 SipeCandidateType
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);
423 SipeNetworkProtocol
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);
431 void
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);
439 static void
440 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
441 gpointer value,
442 gpointer user_data)
444 GList *entry = value;
445 GList **candidates = user_data;
447 g_object_unref(entry->data);
448 *candidates = g_list_delete_link(*candidates, entry);
451 static GList *
452 ensure_candidate_pairs(GList *candidates)
454 GHashTable *lone_cand_links;
455 GList *i;
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);
465 g_free(foundation);
466 } else {
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);
474 return candidates;
477 GList *
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,
482 stream->sessionid,
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);
494 return candidates;
497 void
498 sipe_backend_media_hold(struct sipe_backend_media *media, gboolean local)
500 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HOLD,
501 NULL, NULL, local);
504 void
505 sipe_backend_media_unhold(struct sipe_backend_media *media, gboolean local)
507 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_UNHOLD,
508 NULL, NULL, local);
511 void
512 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
514 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
515 NULL, NULL, local);
518 void
519 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
521 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
522 NULL, NULL, local);
525 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
527 switch (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)
536 switch (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)
546 switch (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)
558 switch (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)
570 switch (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)
580 switch (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;
588 Local Variables:
589 mode: c
590 c-file-style: "bsd"
591 indent-tabs-mode: t
592 tab-width: 8
593 End: