notify: re-order processing of modifiedContact
[siplcs.git] / src / purple / purple-media.c
blobf2f786936909abd6e397ce78bfddde662585afa2
1 /**
2 * @file purple-media.c
4 * pidgin-sipe
6 * Copyright (C) 2010-12 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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include "glib.h"
28 #include "glib/gstdio.h"
29 #include <fcntl.h>
30 #include <string.h>
31 #include <unistd.h>
33 #include "sipe-common.h"
35 #include "mediamanager.h"
36 #include "agent.h"
38 #ifdef _WIN32
39 /* wrappers for write() & friends for socket handling */
40 #include "win32/win32dep.h"
41 #endif
43 #include "sipe-backend.h"
44 #include "sipe-core.h"
46 #include "purple-private.h"
49 * GValueArray has been marked deprecated in glib 2.32 but it is still used by
50 * libpurple and libfarsight APIs. Therefore we need to disable the deprecated
51 * warning so that the code still compiles for platforms that enable it.
53 * GStreamer interfaces fail to compile on ARM architecture with -Wcast-align
55 * Diagnostic #pragma was added in GCC 4.2.0
57 #if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 2)
58 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
59 #if defined(__ARMEL__) || defined(__ARMEB__) || defined(__mips__) || defined(__sparc__)
60 #pragma GCC diagnostic ignored "-Wcast-align"
61 #endif
62 #endif
64 #include "media-gst.h"
66 struct sipe_backend_media {
67 PurpleMedia *m;
68 GSList *streams;
69 /**
70 * Number of media streams that were not yet locally accepted or rejected.
72 guint unconfirmed_streams;
75 struct sipe_backend_stream {
76 gchar *sessionid;
77 gchar *participant;
78 gboolean candidates_prepared;
79 gboolean local_on_hold;
80 gboolean remote_on_hold;
81 gboolean accepted;
84 static void
85 backend_stream_free(struct sipe_backend_stream *stream)
87 if (stream) {
88 g_free(stream->sessionid);
89 g_free(stream->participant);
90 g_free(stream);
94 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type);
95 static PurpleMediaCandidateType sipe_candidate_type_to_purple(SipeCandidateType type);
96 static SipeCandidateType purple_candidate_type_to_sipe(PurpleMediaCandidateType type);
97 static PurpleMediaNetworkProtocol sipe_network_protocol_to_purple(SipeNetworkProtocol proto);
98 static SipeNetworkProtocol purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto);
100 static void
101 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
102 gchar *sessionid,
103 SIPE_UNUSED_PARAMETER gchar *participant,
104 struct sipe_media_call *call)
106 struct sipe_backend_stream *stream;
107 stream = sipe_backend_media_get_stream_by_id(call->backend_private, sessionid);
109 stream->candidates_prepared = TRUE;
111 if (call->candidates_prepared_cb &&
112 sipe_backend_candidates_prepared(call->backend_private)) {
113 call->candidates_prepared_cb(call, stream);
117 static void
118 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
119 PurpleMediaState state,
120 gchar *sessionid,
121 gchar *participant,
122 struct sipe_media_call *call)
124 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state, sessionid, participant);
125 if (state == PURPLE_MEDIA_STATE_END &&
126 !sessionid && !participant && call->media_end_cb)
127 call->media_end_cb(call);
130 void
131 capture_pipeline(const gchar *label) {
132 PurpleMediaManager *manager = purple_media_manager_get();
133 GstElement *pipeline = purple_media_manager_get_pipeline(manager);
134 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, label);
137 static void
138 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *message,
139 struct sipe_media_call *call)
141 capture_pipeline("ERROR");
143 if (call->error_cb)
144 call->error_cb(call, message);
147 static void
148 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
149 PurpleMediaInfoType type,
150 gchar *sessionid,
151 gchar *participant,
152 gboolean local,
153 struct sipe_media_call *call)
155 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
156 if (call->call_accept_cb && !sessionid && !participant)
157 call->call_accept_cb(call, local);
158 else if (sessionid && participant) {
159 struct sipe_backend_stream *stream;
160 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
161 sessionid);
162 if (stream) {
163 if (!stream->accepted && local)
164 --call->backend_private->unconfirmed_streams;
165 stream->accepted = TRUE;
168 } else if (type == PURPLE_MEDIA_INFO_HOLD || type == PURPLE_MEDIA_INFO_UNHOLD) {
170 gboolean state = (type == PURPLE_MEDIA_INFO_HOLD);
172 if (sessionid) {
173 // Hold specific stream
174 struct sipe_backend_stream *stream;
175 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
176 sessionid);
178 if (local)
179 stream->local_on_hold = state;
180 else
181 stream->remote_on_hold = state;
182 } else {
183 // Hold all streams
184 GSList *i = sipe_backend_media_get_streams(call->backend_private);
185 for (; i; i = i->next) {
186 struct sipe_backend_stream *stream = i->data;
188 if (local)
189 stream->local_on_hold = state;
190 else
191 stream->remote_on_hold = state;
195 if (call->call_hold_cb)
196 call->call_hold_cb(call, local, state);
197 } else if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) {
198 if (!sessionid && !participant) {
199 if (type == PURPLE_MEDIA_INFO_HANGUP && call->call_hangup_cb)
200 call->call_hangup_cb(call, local);
201 else if (type == PURPLE_MEDIA_INFO_REJECT && call->call_reject_cb && !local)
202 call->call_reject_cb(call, local);
203 } else if (sessionid && participant) {
204 struct sipe_backend_stream *stream;
205 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
206 sessionid);
208 if (stream) {
209 call->backend_private->streams = g_slist_remove(call->backend_private->streams, stream);
210 backend_stream_free(stream);
211 if (local && --call->backend_private->unconfirmed_streams == 0 &&
212 call->call_reject_cb)
213 call->call_reject_cb(call, local);
219 struct sipe_backend_media *
220 sipe_backend_media_new(struct sipe_core_public *sipe_public,
221 struct sipe_media_call *call,
222 const gchar *participant,
223 gboolean initiator)
225 struct sipe_backend_media *media = g_new0(struct sipe_backend_media, 1);
226 struct sipe_backend_private *purple_private = sipe_public->backend_private;
227 PurpleMediaManager *manager = purple_media_manager_get();
228 GstElement *pipeline;
230 media->m = purple_media_manager_create_media(manager,
231 purple_private->account,
232 "fsrtpconference",
233 participant, initiator);
235 g_signal_connect(G_OBJECT(media->m), "candidates-prepared",
236 G_CALLBACK(on_candidates_prepared_cb), call);
237 g_signal_connect(G_OBJECT(media->m), "stream-info",
238 G_CALLBACK(on_stream_info_cb), call);
239 g_signal_connect(G_OBJECT(media->m), "error",
240 G_CALLBACK(on_error_cb), call);
241 g_signal_connect(G_OBJECT(media->m), "state-changed",
242 G_CALLBACK(on_state_changed_cb), call);
244 /* On error, the pipeline is no longer in PLAYING state and libpurple
245 * will not switch it back to PLAYING, preventing any more calls until
246 * application restart. We switch the state ourselves here to negate
247 * effect of any error in previous call (if any). */
248 pipeline = purple_media_manager_get_pipeline(manager);
249 gst_element_set_state(pipeline, GST_STATE_PLAYING);
251 return media;
254 void
255 sipe_backend_media_free(struct sipe_backend_media *media)
257 if (media) {
258 GSList *stream = media->streams;
260 for (; stream; stream = g_slist_delete_link(stream, stream))
261 backend_stream_free(stream->data);
263 g_free(media);
267 void
268 sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname)
270 if (media) {
271 guint num_params = 3;
272 GParameter *params = g_new0(GParameter, num_params);
273 params[0].name = "sdes-cname";
274 g_value_init(&params[0].value, G_TYPE_STRING);
275 g_value_set_string(&params[0].value, cname);
276 params[1].name = "sdes-name";
277 g_value_init(&params[1].value, G_TYPE_STRING);
278 params[2].name = "sdes-tool";
279 g_value_init(&params[2].value, G_TYPE_STRING);
281 purple_media_set_params(media->m, num_params, params);
283 g_value_unset(&params[0].value);
284 g_free(params);
288 #define FS_CODECS_CONF \
289 "# Automatically created by SIPE plugin\n" \
290 "[video/H263]\n" \
291 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
292 "\n" \
293 "[audio/PCMA]\n" \
294 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
295 "\n" \
296 "[audio/PCMU]\n" \
297 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
299 static void
300 ensure_codecs_conf()
302 gchar *filename;
303 filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
305 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
306 int fd = g_open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
307 gchar *fs_codecs_conf = FS_CODECS_CONF;
308 if ((fd < 0) || write(fd, fs_codecs_conf, strlen(fs_codecs_conf)) == -1)
309 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
310 if (fd >= 0)
311 close(fd);
314 g_free(filename);
317 static void
318 append_relay(GValueArray *relay_info, const gchar *ip, guint port, gchar *type,
319 gchar *username, gchar *password)
321 GValue value;
322 GstStructure *gst_relay_info;
324 gst_relay_info = gst_structure_new("relay-info",
325 "ip", G_TYPE_STRING, ip,
326 "port", G_TYPE_UINT, port,
327 "relay-type", G_TYPE_STRING, type,
328 "username", G_TYPE_STRING, username,
329 "password", G_TYPE_STRING, password,
330 NULL);
332 if (gst_relay_info) {
333 memset(&value, 0, sizeof(GValue));
334 g_value_init(&value, GST_TYPE_STRUCTURE);
335 gst_value_set_structure(&value, gst_relay_info);
337 g_value_array_append(relay_info, &value);
338 gst_structure_free(gst_relay_info);
342 struct sipe_backend_media_relays *
343 sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password)
345 GValueArray *relay_info = g_value_array_new(0);
347 for (; media_relays; media_relays = media_relays->next) {\
348 struct sipe_media_relay *relay = media_relays->data;
350 /* Skip relays where IP could not be resolved. */
351 if (!relay->hostname)
352 continue;
354 if (relay->udp_port != 0)
355 append_relay(relay_info, relay->hostname, relay->udp_port,
356 "udp", username, password);
358 #ifdef HAVE_ICE_TCP
359 if (relay->tcp_port != 0)
360 append_relay(relay_info, relay->hostname, relay->tcp_port,
361 "tcp", username, password);
362 #endif
365 return (struct sipe_backend_media_relays *)relay_info;
368 void
369 sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays)
371 g_value_array_free((GValueArray *)media_relays);
374 static guint
375 stream_demultiplex_cb(const gchar *buf, SIPE_UNUSED_PARAMETER gpointer *user_data)
377 guint8 payload_type = buf[1] & 0x7F;
378 if (payload_type >= 200 && payload_type <=204) {
379 // Looks like RTCP
380 return PURPLE_MEDIA_COMPONENT_RTCP;
381 } else {
382 // Looks like RTP
383 return PURPLE_MEDIA_COMPONENT_RTP;
387 struct sipe_backend_stream *
388 sipe_backend_media_add_stream(struct sipe_backend_media *media,
389 const gchar *id,
390 const gchar *participant,
391 SipeMediaType type,
392 SipeIceVersion ice_version,
393 gboolean initiator,
394 struct sipe_backend_media_relays *media_relays)
396 struct sipe_backend_stream *stream = NULL;
397 PurpleMediaSessionType prpl_type = sipe_media_to_purple(type);
398 GParameter *params = NULL;
399 guint params_cnt = 0;
400 gchar *transmitter;
402 if (ice_version != SIPE_ICE_NO_ICE) {
403 transmitter = "nice";
404 params_cnt = 4;
406 params = g_new0(GParameter, params_cnt);
408 params[0].name = "compatibility-mode";
409 g_value_init(&params[0].value, G_TYPE_UINT);
410 g_value_set_uint(&params[0].value,
411 ice_version == SIPE_ICE_DRAFT_6 ?
412 NICE_COMPATIBILITY_OC2007 :
413 NICE_COMPATIBILITY_OC2007R2);
415 params[1].name = "transport-protocols";
416 g_value_init(&params[1].value, G_TYPE_UINT);
417 #ifdef HAVE_ICE_TCP
418 g_value_set_uint(&params[1].value,
419 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP |
420 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE |
421 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE);
422 #else
423 g_value_set_uint(&params[1].value,
424 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP);
425 #endif
427 params[2].name = "demultiplex-func";
428 g_value_init(&params[2].value, G_TYPE_POINTER);
429 g_value_set_pointer(&params[2].value, stream_demultiplex_cb);
431 if (media_relays) {
432 params[3].name = "relay-info";
433 g_value_init(&params[3].value, G_TYPE_VALUE_ARRAY);
434 g_value_set_boxed(&params[3].value, media_relays);
435 } else
436 --params_cnt;
437 } else {
438 // TODO: session naming here, Communicator needs audio/video
439 transmitter = "rawudp";
440 //sessionid = "sipe-voice-rawudp";
442 /* To avoid Coverity FORWARD_NULL warning. params_cnt is
443 * still 0 so this is a no-op. libpurple API documentation
444 * doesn't specify if params can be NULL or not. */
445 params = g_new0(GParameter, 1);
448 ensure_codecs_conf();
450 if (purple_media_add_stream(media->m, id, participant, prpl_type,
451 initiator, transmitter, params_cnt,
452 params)) {
453 stream = g_new0(struct sipe_backend_stream, 1);
454 stream->sessionid = g_strdup(id);
455 stream->participant = g_strdup(participant);
456 stream->candidates_prepared = FALSE;
458 media->streams = g_slist_append(media->streams, stream);
459 if (!initiator)
460 ++media->unconfirmed_streams;
463 if ((params_cnt > 2) && media_relays)
464 g_value_unset(&params[3].value);
466 g_free(params);
468 return stream;
471 void
472 sipe_backend_media_remove_stream(struct sipe_backend_media *media,
473 struct sipe_backend_stream *stream)
475 g_return_if_fail(media && stream);
477 purple_media_end(media->m, stream->sessionid, NULL);
478 media->streams = g_slist_remove(media->streams, stream);
479 backend_stream_free(stream);
482 GSList *sipe_backend_media_get_streams(struct sipe_backend_media *media)
484 return media->streams;
487 struct sipe_backend_stream *
488 sipe_backend_media_get_stream_by_id(struct sipe_backend_media *media,
489 const gchar *id)
491 GSList *i;
492 for (i = media->streams; i; i = i->next) {
493 struct sipe_backend_stream *stream = i->data;
494 if (sipe_strequal(stream->sessionid, id))
495 return stream;
497 return NULL;
500 void
501 sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
502 struct sipe_backend_stream *stream,
503 GList *candidates)
505 GList *udp_candidates = NULL;
507 #ifndef HAVE_ICE_TCP
508 while (candidates) {
509 PurpleMediaCandidate *candidate = candidates->data;
510 PurpleMediaNetworkProtocol proto;
512 proto = purple_media_candidate_get_protocol(candidate);
513 if (proto == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP)
514 udp_candidates = g_list_append(udp_candidates, candidate);
516 candidates = candidates->next;
519 candidates = udp_candidates;
520 #endif
523 purple_media_add_remote_candidates(media->m, stream->sessionid,
524 stream->participant, candidates);
526 g_list_free(udp_candidates);
529 gboolean sipe_backend_media_is_initiator(struct sipe_backend_media *media,
530 struct sipe_backend_stream *stream)
532 return purple_media_is_initiator(media->m,
533 stream ? stream->sessionid : NULL,
534 stream ? stream->participant : NULL);
537 gboolean sipe_backend_media_accepted(struct sipe_backend_media *media)
539 return purple_media_accepted(media->m, NULL, NULL);
542 gboolean
543 sipe_backend_candidates_prepared(struct sipe_backend_media *media)
545 GSList *streams = media->streams;
546 for (; streams; streams = streams->next) {
547 struct sipe_backend_stream *s = streams->data;
548 if (!s->candidates_prepared)
549 return FALSE;
551 return TRUE;
554 GList *
555 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media *media,
556 struct sipe_backend_stream *stream)
558 return purple_media_get_active_local_candidates(media->m,
559 stream->sessionid,
560 stream->participant);
563 GList *
564 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media *media,
565 struct sipe_backend_stream *stream)
567 return purple_media_get_active_remote_candidates(media->m,
568 stream->sessionid,
569 stream->participant);
572 const gchar *
573 sipe_backend_stream_get_id(struct sipe_backend_stream *stream)
575 return stream->sessionid;
578 void sipe_backend_stream_hold(struct sipe_backend_media *media,
579 struct sipe_backend_stream *stream,
580 gboolean local)
582 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HOLD,
583 stream->sessionid, stream->participant,
584 local);
587 void sipe_backend_stream_unhold(struct sipe_backend_media *media,
588 struct sipe_backend_stream *stream,
589 gboolean local)
591 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_UNHOLD,
592 stream->sessionid, stream->participant,
593 local);
596 gboolean sipe_backend_stream_is_held(struct sipe_backend_stream *stream)
598 g_return_val_if_fail(stream, FALSE);
600 return stream->local_on_hold || stream->remote_on_hold;
603 struct sipe_backend_codec *
604 sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate)
606 return (struct sipe_backend_codec *)purple_media_codec_new(id, name,
607 sipe_media_to_purple(type),
608 clock_rate);
611 void
612 sipe_backend_codec_free(struct sipe_backend_codec *codec)
614 if (codec)
615 g_object_unref(codec);
619 sipe_backend_codec_get_id(struct sipe_backend_codec *codec)
621 return purple_media_codec_get_id((PurpleMediaCodec *)codec);
624 gchar *
625 sipe_backend_codec_get_name(struct sipe_backend_codec *codec)
627 /* Not explicitly documented, but return value must be g_free()'d */
628 return purple_media_codec_get_encoding_name((PurpleMediaCodec *)codec);
631 guint
632 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
634 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
637 void
638 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec,
639 const gchar *name, const gchar *value)
641 purple_media_codec_add_optional_parameter((PurpleMediaCodec *)codec, name, value);
644 GList *
645 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
647 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
650 gboolean
651 sipe_backend_set_remote_codecs(struct sipe_backend_media *media,
652 struct sipe_backend_stream *stream,
653 GList *codecs)
655 return purple_media_set_remote_codecs(media->m,
656 stream->sessionid,
657 stream->participant,
658 codecs);
661 GList*
662 sipe_backend_get_local_codecs(struct sipe_backend_media *media,
663 struct sipe_backend_stream *stream)
665 GList *codecs = purple_media_get_codecs(media->m,
666 stream->sessionid);
667 GList *i = codecs;
668 gboolean is_conference = (g_strstr_len(stream->participant,
669 strlen(stream->participant),
670 "app:conf:audio-video:") != NULL);
673 * Do not announce Theora. Its optional parameters are too long,
674 * Communicator rejects such SDP message and does not support the codec
675 * anyway.
677 * For some yet unknown reason, A/V conferencing server does not accept
678 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
679 * we are able to decode incoming SIREN from server and with MSOC
680 * client, bidirectional call using the codec works. Until resolved,
681 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
682 * seems to work properly in this scenario.
684 while (i) {
685 PurpleMediaCodec *codec = i->data;
686 gchar *encoding_name = purple_media_codec_get_encoding_name(codec);
688 if (sipe_strequal(encoding_name,"THEORA") ||
689 (is_conference && sipe_strequal(encoding_name,"SIREN"))) {
690 GList *tmp;
691 g_object_unref(codec);
692 tmp = i->next;
693 codecs = g_list_delete_link(codecs, i);
694 i = tmp;
695 } else
696 i = i->next;
698 g_free(encoding_name);
701 return codecs;
704 struct sipe_backend_candidate *
705 sipe_backend_candidate_new(const gchar *foundation,
706 SipeComponentType component,
707 SipeCandidateType type, SipeNetworkProtocol proto,
708 const gchar *ip, guint port,
709 const gchar *username,
710 const gchar *password)
712 PurpleMediaCandidate *c = purple_media_candidate_new(
713 /* Libnice and Farsight rely on non-NULL foundation to
714 * distinguish between candidates of a component. When NULL
715 * foundation is passed (ie. ICE draft 6 does not use foudation),
716 * use username instead. If no foundation is provided, Farsight
717 * may signal an active candidate different from the one actually
718 * in use. See Farsight's agent_new_selected_pair() in
719 * fs-nice-stream-transmitter.h where first candidate in the
720 * remote list is always selected when no foundation. */
721 foundation ? foundation : username,
722 component,
723 sipe_candidate_type_to_purple(type),
724 sipe_network_protocol_to_purple(proto),
726 port);
727 g_object_set(c, "username", username, "password", password, NULL);
728 return (struct sipe_backend_candidate *)c;
731 void
732 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
734 if (candidate)
735 g_object_unref(candidate);
738 gchar *
739 sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate)
741 /* Not explicitly documented, but return value must be g_free()'d */
742 return purple_media_candidate_get_username((PurpleMediaCandidate*)candidate);
745 gchar *
746 sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate)
748 /* Not explicitly documented, but return value must be g_free()'d */
749 return purple_media_candidate_get_password((PurpleMediaCandidate*)candidate);
752 gchar *
753 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate)
755 /* Not explicitly documented, but return value must be g_free()'d */
756 return purple_media_candidate_get_foundation((PurpleMediaCandidate*)candidate);
759 gchar *
760 sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate)
762 /* Not explicitly documented, but return value must be g_free()'d */
763 return purple_media_candidate_get_ip((PurpleMediaCandidate*)candidate);
766 guint
767 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
769 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
772 gchar *
773 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate)
775 /* Not explicitly documented, but return value must be g_free()'d */
776 return purple_media_candidate_get_base_ip((PurpleMediaCandidate*)candidate);
779 guint
780 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
782 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
785 guint32
786 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
788 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
791 void
792 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
794 g_object_set(candidate, "priority", priority, NULL);
797 SipeComponentType
798 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
800 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
803 SipeCandidateType
804 sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate)
806 PurpleMediaCandidateType type =
807 purple_media_candidate_get_candidate_type((PurpleMediaCandidate*)candidate);
808 return purple_candidate_type_to_sipe(type);
811 SipeNetworkProtocol
812 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate)
814 PurpleMediaNetworkProtocol proto =
815 purple_media_candidate_get_protocol((PurpleMediaCandidate*)candidate);
816 return purple_network_protocol_to_sipe(proto);
819 static void
820 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
821 gpointer value,
822 gpointer user_data)
824 GList *entry = value;
825 GList **candidates = user_data;
827 g_object_unref(entry->data);
828 *candidates = g_list_delete_link(*candidates, entry);
831 static GList *
832 ensure_candidate_pairs(GList *candidates)
834 GHashTable *lone_cand_links;
835 GList *i;
837 lone_cand_links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
839 for (i = candidates; i; i = i->next) {
840 PurpleMediaCandidate *c = i->data;
841 gchar *foundation = purple_media_candidate_get_foundation(c);
843 if (g_hash_table_lookup(lone_cand_links, foundation)) {
844 g_hash_table_remove(lone_cand_links, foundation);
845 g_free(foundation);
846 } else {
847 g_hash_table_insert(lone_cand_links, foundation, i);
851 g_hash_table_foreach(lone_cand_links, remove_lone_candidate_cb, &candidates);
852 g_hash_table_destroy(lone_cand_links);
854 return candidates;
857 GList *
858 sipe_backend_get_local_candidates(struct sipe_backend_media *media,
859 struct sipe_backend_stream *stream)
861 GList *candidates = purple_media_get_local_candidates(media->m,
862 stream->sessionid,
863 stream->participant);
865 * Sometimes purple will not return complete list of candidates, even
866 * after "candidates-prepared" signal is emitted. This is a feature of
867 * libnice, namely affecting candidates discovered via UPnP. Nice does
868 * not wait until discovery is finished and can signal end of candidate
869 * gathering before all responses from UPnP enabled gateways are received.
871 * Remove any incomplete RTP+RTCP candidate pairs from the list.
873 candidates = ensure_candidate_pairs(candidates);
874 return candidates;
877 void
878 sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local)
880 if (media)
881 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT,
882 NULL, NULL, local);
885 void
886 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
888 if (media)
889 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
890 NULL, NULL, local);
893 void
894 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
896 if (media)
897 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
898 NULL, NULL, local);
901 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
903 switch (type) {
904 case SIPE_MEDIA_AUDIO: return PURPLE_MEDIA_AUDIO;
905 case SIPE_MEDIA_VIDEO: return PURPLE_MEDIA_VIDEO;
906 default: return PURPLE_MEDIA_NONE;
910 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
912 switch (type) {
913 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
914 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
915 default: return SIPE_MEDIA_AUDIO;
919 static PurpleMediaCandidateType
920 sipe_candidate_type_to_purple(SipeCandidateType type)
922 switch (type) {
923 case SIPE_CANDIDATE_TYPE_HOST: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
924 case SIPE_CANDIDATE_TYPE_RELAY: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
925 case SIPE_CANDIDATE_TYPE_SRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
926 case SIPE_CANDIDATE_TYPE_PRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX;
927 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
931 static SipeCandidateType
932 purple_candidate_type_to_sipe(PurpleMediaCandidateType type)
934 switch (type) {
935 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: return SIPE_CANDIDATE_TYPE_HOST;
936 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: return SIPE_CANDIDATE_TYPE_RELAY;
937 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: return SIPE_CANDIDATE_TYPE_SRFLX;
938 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: return SIPE_CANDIDATE_TYPE_PRFLX;
939 default: return SIPE_CANDIDATE_TYPE_HOST;
943 static PurpleMediaNetworkProtocol
944 sipe_network_protocol_to_purple(SipeNetworkProtocol proto)
946 switch (proto) {
947 #ifdef HAVE_ICE_TCP
948 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
949 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE;
950 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
951 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE;
952 #else
953 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
954 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
955 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP;
956 #endif
957 case SIPE_NETWORK_PROTOCOL_UDP:
958 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
959 default:
960 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
964 static SipeNetworkProtocol
965 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto)
967 switch (proto) {
968 #ifdef HAVE_ICE_TCP
969 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE:
970 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
971 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE:
972 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
973 #else
974 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP:
975 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
976 #endif
977 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
978 return SIPE_NETWORK_PROTOCOL_UDP;
979 default:
980 return SIPE_NETWORK_PROTOCOL_UDP;
985 Local Variables:
986 mode: c
987 c-file-style: "bsd"
988 indent-tabs-mode: t
989 tab-width: 8
990 End: