purple: fix build break
[siplcs.git] / src / purple / purple-media.c
blob4c3abcd5ee13b9adebd2909d4e86a07efb6665c9
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 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
35 #include "sipe-common.h"
37 #include "mediamanager.h"
38 #include "agent.h"
40 #ifdef _WIN32
41 /* wrappers for write() & friends for socket handling */
42 #include "win32/win32dep.h"
43 #endif
45 #include "sipe-backend.h"
46 #include "sipe-core.h"
48 #include "purple-private.h"
51 * GStreamer interfaces fail to compile on ARM architecture with -Wcast-align
53 * Diagnostic #pragma was added in GCC 4.2.0
55 #if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 2)
56 #if defined(__ARMEL__) || defined(__ARMEB__) || defined(__mips__) || defined(__sparc__)
57 #pragma GCC diagnostic ignored "-Wcast-align"
58 #endif
59 #endif
61 #include "media-gst.h"
63 struct sipe_backend_media {
64 PurpleMedia *m;
65 GSList *streams;
66 /**
67 * Number of media streams that were not yet locally accepted or rejected.
69 guint unconfirmed_streams;
72 struct sipe_backend_stream {
73 gchar *sessionid;
74 gchar *participant;
75 gboolean candidates_prepared;
76 gboolean local_on_hold;
77 gboolean remote_on_hold;
78 gboolean accepted;
81 static void
82 backend_stream_free(struct sipe_backend_stream *stream)
84 if (stream) {
85 g_free(stream->sessionid);
86 g_free(stream->participant);
87 g_free(stream);
91 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type);
92 static PurpleMediaCandidateType sipe_candidate_type_to_purple(SipeCandidateType type);
93 static SipeCandidateType purple_candidate_type_to_sipe(PurpleMediaCandidateType type);
94 static PurpleMediaNetworkProtocol sipe_network_protocol_to_purple(SipeNetworkProtocol proto);
95 static SipeNetworkProtocol purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto);
97 static void
98 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
99 gchar *sessionid,
100 SIPE_UNUSED_PARAMETER gchar *participant,
101 struct sipe_media_call *call)
103 struct sipe_backend_stream *stream;
104 stream = sipe_backend_media_get_stream_by_id(call->backend_private, sessionid);
106 stream->candidates_prepared = TRUE;
108 if (call->candidates_prepared_cb &&
109 sipe_backend_candidates_prepared(call->backend_private)) {
110 call->candidates_prepared_cb(call, stream);
114 static void
115 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
116 PurpleMediaState state,
117 gchar *sessionid,
118 gchar *participant,
119 struct sipe_media_call *call)
121 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state, sessionid, participant);
122 if (state == PURPLE_MEDIA_STATE_END &&
123 !sessionid && !participant && call->media_end_cb)
124 call->media_end_cb(call);
127 void
128 capture_pipeline(const gchar *label) {
129 PurpleMediaManager *manager = purple_media_manager_get();
130 GstElement *pipeline = purple_media_manager_get_pipeline(manager);
131 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, label);
134 static void
135 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *message,
136 struct sipe_media_call *call)
138 capture_pipeline("ERROR");
140 if (call->error_cb)
141 call->error_cb(call, message);
144 static void
145 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
146 PurpleMediaInfoType type,
147 gchar *sessionid,
148 gchar *participant,
149 gboolean local,
150 struct sipe_media_call *call)
152 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
153 if (call->call_accept_cb && !sessionid && !participant)
154 call->call_accept_cb(call, local);
155 else if (sessionid && participant) {
156 struct sipe_backend_stream *stream;
157 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
158 sessionid);
159 if (stream) {
160 if (!stream->accepted && local)
161 --call->backend_private->unconfirmed_streams;
162 stream->accepted = TRUE;
165 } else if (type == PURPLE_MEDIA_INFO_HOLD || type == PURPLE_MEDIA_INFO_UNHOLD) {
167 gboolean state = (type == PURPLE_MEDIA_INFO_HOLD);
169 if (sessionid) {
170 // Hold specific stream
171 struct sipe_backend_stream *stream;
172 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
173 sessionid);
175 if (local)
176 stream->local_on_hold = state;
177 else
178 stream->remote_on_hold = state;
179 } else {
180 // Hold all streams
181 GSList *i = sipe_backend_media_get_streams(call->backend_private);
182 for (; i; i = i->next) {
183 struct sipe_backend_stream *stream = i->data;
185 if (local)
186 stream->local_on_hold = state;
187 else
188 stream->remote_on_hold = state;
192 if (call->call_hold_cb)
193 call->call_hold_cb(call, local, state);
194 } else if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) {
195 if (!sessionid && !participant) {
196 if (type == PURPLE_MEDIA_INFO_HANGUP && call->call_hangup_cb)
197 call->call_hangup_cb(call, local);
198 else if (type == PURPLE_MEDIA_INFO_REJECT && call->call_reject_cb && !local)
199 call->call_reject_cb(call, local);
200 } else if (sessionid && participant) {
201 struct sipe_backend_stream *stream;
202 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
203 sessionid);
205 if (stream) {
206 call->backend_private->streams = g_slist_remove(call->backend_private->streams, stream);
207 backend_stream_free(stream);
208 if (local && --call->backend_private->unconfirmed_streams == 0 &&
209 call->call_reject_cb)
210 call->call_reject_cb(call, local);
216 struct sipe_backend_media *
217 sipe_backend_media_new(struct sipe_core_public *sipe_public,
218 struct sipe_media_call *call,
219 const gchar *participant,
220 gboolean initiator)
222 struct sipe_backend_media *media = g_new0(struct sipe_backend_media, 1);
223 struct sipe_backend_private *purple_private = sipe_public->backend_private;
224 PurpleMediaManager *manager = purple_media_manager_get();
225 GstElement *pipeline;
227 media->m = purple_media_manager_create_media(manager,
228 purple_private->account,
229 "fsrtpconference",
230 participant, initiator);
232 g_signal_connect(G_OBJECT(media->m), "candidates-prepared",
233 G_CALLBACK(on_candidates_prepared_cb), call);
234 g_signal_connect(G_OBJECT(media->m), "stream-info",
235 G_CALLBACK(on_stream_info_cb), call);
236 g_signal_connect(G_OBJECT(media->m), "error",
237 G_CALLBACK(on_error_cb), call);
238 g_signal_connect(G_OBJECT(media->m), "state-changed",
239 G_CALLBACK(on_state_changed_cb), call);
241 /* On error, the pipeline is no longer in PLAYING state and libpurple
242 * will not switch it back to PLAYING, preventing any more calls until
243 * application restart. We switch the state ourselves here to negate
244 * effect of any error in previous call (if any). */
245 pipeline = purple_media_manager_get_pipeline(manager);
246 gst_element_set_state(pipeline, GST_STATE_PLAYING);
248 return media;
251 void
252 sipe_backend_media_free(struct sipe_backend_media *media)
254 if (media) {
255 GSList *stream = media->streams;
257 for (; stream; stream = g_slist_delete_link(stream, stream))
258 backend_stream_free(stream->data);
260 g_free(media);
264 void
265 sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname)
267 if (media) {
268 guint num_params = 3;
269 GParameter *params = g_new0(GParameter, num_params);
270 params[0].name = "sdes-cname";
271 g_value_init(&params[0].value, G_TYPE_STRING);
272 g_value_set_string(&params[0].value, cname);
273 params[1].name = "sdes-name";
274 g_value_init(&params[1].value, G_TYPE_STRING);
275 params[2].name = "sdes-tool";
276 g_value_init(&params[2].value, G_TYPE_STRING);
278 purple_media_set_params(media->m, num_params, params);
280 g_value_unset(&params[0].value);
281 g_free(params);
285 #define FS_CODECS_CONF \
286 "# Automatically created by SIPE plugin\n" \
287 "[video/H263]\n" \
288 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
289 "\n" \
290 "[audio/PCMA]\n" \
291 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
292 "\n" \
293 "[audio/PCMU]\n" \
294 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
296 static void
297 ensure_codecs_conf()
299 gchar *filename;
300 filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
302 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
303 int fd = g_open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
304 gchar *fs_codecs_conf = FS_CODECS_CONF;
305 if ((fd < 0) || write(fd, fs_codecs_conf, strlen(fs_codecs_conf)) == -1)
306 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
307 if (fd >= 0)
308 close(fd);
311 g_free(filename);
314 static void
315 append_relay(GValueArray *relay_info, const gchar *ip, guint port, gchar *type,
316 gchar *username, gchar *password)
318 GValue value;
319 GstStructure *gst_relay_info;
321 gst_relay_info = gst_structure_new("relay-info",
322 "ip", G_TYPE_STRING, ip,
323 "port", G_TYPE_UINT, port,
324 "relay-type", G_TYPE_STRING, type,
325 "username", G_TYPE_STRING, username,
326 "password", G_TYPE_STRING, password,
327 NULL);
329 if (gst_relay_info) {
330 memset(&value, 0, sizeof(GValue));
331 g_value_init(&value, GST_TYPE_STRUCTURE);
332 gst_value_set_structure(&value, gst_relay_info);
334 g_value_array_append(relay_info, &value);
335 gst_structure_free(gst_relay_info);
339 struct sipe_backend_media_relays *
340 sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password)
342 GValueArray *relay_info = g_value_array_new(0);
344 for (; media_relays; media_relays = media_relays->next) {\
345 struct sipe_media_relay *relay = media_relays->data;
347 /* Skip relays where IP could not be resolved. */
348 if (!relay->hostname)
349 continue;
351 if (relay->udp_port != 0)
352 append_relay(relay_info, relay->hostname, relay->udp_port,
353 "udp", username, password);
355 #ifdef HAVE_ICE_TCP
356 if (relay->tcp_port != 0)
357 append_relay(relay_info, relay->hostname, relay->tcp_port,
358 "tcp", username, password);
359 #endif
362 return (struct sipe_backend_media_relays *)relay_info;
365 void
366 sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays)
368 g_value_array_free((GValueArray *)media_relays);
371 static guint
372 stream_demultiplex_cb(const gchar *buf, SIPE_UNUSED_PARAMETER gpointer *user_data)
374 guint8 payload_type = buf[1] & 0x7F;
375 if (payload_type >= 200 && payload_type <=204) {
376 // Looks like RTCP
377 return PURPLE_MEDIA_COMPONENT_RTCP;
378 } else {
379 // Looks like RTP
380 return PURPLE_MEDIA_COMPONENT_RTP;
384 struct sipe_backend_stream *
385 sipe_backend_media_add_stream(struct sipe_backend_media *media,
386 const gchar *id,
387 const gchar *participant,
388 SipeMediaType type,
389 SipeIceVersion ice_version,
390 gboolean initiator,
391 struct sipe_backend_media_relays *media_relays)
393 struct sipe_backend_stream *stream = NULL;
394 PurpleMediaSessionType prpl_type = sipe_media_to_purple(type);
395 GParameter *params = NULL;
396 guint params_cnt = 0;
397 gchar *transmitter;
399 if (ice_version != SIPE_ICE_NO_ICE) {
400 transmitter = "nice";
401 params_cnt = 4;
403 params = g_new0(GParameter, params_cnt);
405 params[0].name = "compatibility-mode";
406 g_value_init(&params[0].value, G_TYPE_UINT);
407 g_value_set_uint(&params[0].value,
408 ice_version == SIPE_ICE_DRAFT_6 ?
409 NICE_COMPATIBILITY_OC2007 :
410 NICE_COMPATIBILITY_OC2007R2);
412 params[1].name = "transport-protocols";
413 g_value_init(&params[1].value, G_TYPE_UINT);
414 #ifdef HAVE_ICE_TCP
415 g_value_set_uint(&params[1].value,
416 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP |
417 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE |
418 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE);
419 #else
420 g_value_set_uint(&params[1].value,
421 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP);
422 #endif
424 params[2].name = "demultiplex-func";
425 g_value_init(&params[2].value, G_TYPE_POINTER);
426 g_value_set_pointer(&params[2].value, stream_demultiplex_cb);
428 if (media_relays) {
429 params[3].name = "relay-info";
430 g_value_init(&params[3].value, G_TYPE_VALUE_ARRAY);
431 g_value_set_boxed(&params[3].value, media_relays);
432 } else
433 --params_cnt;
434 } else {
435 // TODO: session naming here, Communicator needs audio/video
436 transmitter = "rawudp";
437 //sessionid = "sipe-voice-rawudp";
439 /* To avoid Coverity FORWARD_NULL warning. params_cnt is
440 * still 0 so this is a no-op. libpurple API documentation
441 * doesn't specify if params can be NULL or not. */
442 params = g_new0(GParameter, 1);
445 ensure_codecs_conf();
447 if (purple_media_add_stream(media->m, id, participant, prpl_type,
448 initiator, transmitter, params_cnt,
449 params)) {
450 stream = g_new0(struct sipe_backend_stream, 1);
451 stream->sessionid = g_strdup(id);
452 stream->participant = g_strdup(participant);
453 stream->candidates_prepared = FALSE;
455 media->streams = g_slist_append(media->streams, stream);
456 if (!initiator)
457 ++media->unconfirmed_streams;
460 if ((params_cnt > 2) && media_relays)
461 g_value_unset(&params[3].value);
463 g_free(params);
465 return stream;
468 void
469 sipe_backend_media_remove_stream(struct sipe_backend_media *media,
470 struct sipe_backend_stream *stream)
472 g_return_if_fail(media && stream);
474 purple_media_end(media->m, stream->sessionid, NULL);
475 media->streams = g_slist_remove(media->streams, stream);
476 backend_stream_free(stream);
479 GSList *sipe_backend_media_get_streams(struct sipe_backend_media *media)
481 return media->streams;
484 struct sipe_backend_stream *
485 sipe_backend_media_get_stream_by_id(struct sipe_backend_media *media,
486 const gchar *id)
488 GSList *i;
489 for (i = media->streams; i; i = i->next) {
490 struct sipe_backend_stream *stream = i->data;
491 if (sipe_strequal(stream->sessionid, id))
492 return stream;
494 return NULL;
497 void
498 sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
499 struct sipe_backend_stream *stream,
500 GList *candidates)
502 GList *udp_candidates = NULL;
504 #ifndef HAVE_ICE_TCP
505 while (candidates) {
506 PurpleMediaCandidate *candidate = candidates->data;
507 PurpleMediaNetworkProtocol proto;
509 proto = purple_media_candidate_get_protocol(candidate);
510 if (proto == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP)
511 udp_candidates = g_list_append(udp_candidates, candidate);
513 candidates = candidates->next;
516 candidates = udp_candidates;
517 #endif
520 purple_media_add_remote_candidates(media->m, stream->sessionid,
521 stream->participant, candidates);
523 g_list_free(udp_candidates);
526 gboolean sipe_backend_media_is_initiator(struct sipe_backend_media *media,
527 struct sipe_backend_stream *stream)
529 return purple_media_is_initiator(media->m,
530 stream ? stream->sessionid : NULL,
531 stream ? stream->participant : NULL);
534 gboolean sipe_backend_media_accepted(struct sipe_backend_media *media)
536 return purple_media_accepted(media->m, NULL, NULL);
539 gboolean
540 sipe_backend_candidates_prepared(struct sipe_backend_media *media)
542 GSList *streams = media->streams;
543 for (; streams; streams = streams->next) {
544 struct sipe_backend_stream *s = streams->data;
545 if (!s->candidates_prepared)
546 return FALSE;
548 return TRUE;
551 GList *
552 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media *media,
553 struct sipe_backend_stream *stream)
555 return purple_media_get_active_local_candidates(media->m,
556 stream->sessionid,
557 stream->participant);
560 GList *
561 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media *media,
562 struct sipe_backend_stream *stream)
564 return purple_media_get_active_remote_candidates(media->m,
565 stream->sessionid,
566 stream->participant);
569 const gchar *
570 sipe_backend_stream_get_id(struct sipe_backend_stream *stream)
572 return stream->sessionid;
575 void sipe_backend_stream_hold(struct sipe_backend_media *media,
576 struct sipe_backend_stream *stream,
577 gboolean local)
579 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HOLD,
580 stream->sessionid, stream->participant,
581 local);
584 void sipe_backend_stream_unhold(struct sipe_backend_media *media,
585 struct sipe_backend_stream *stream,
586 gboolean local)
588 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_UNHOLD,
589 stream->sessionid, stream->participant,
590 local);
593 gboolean sipe_backend_stream_is_held(struct sipe_backend_stream *stream)
595 g_return_val_if_fail(stream, FALSE);
597 return stream->local_on_hold || stream->remote_on_hold;
600 struct sipe_backend_codec *
601 sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate)
603 return (struct sipe_backend_codec *)purple_media_codec_new(id, name,
604 sipe_media_to_purple(type),
605 clock_rate);
608 void
609 sipe_backend_codec_free(struct sipe_backend_codec *codec)
611 if (codec)
612 g_object_unref(codec);
616 sipe_backend_codec_get_id(struct sipe_backend_codec *codec)
618 return purple_media_codec_get_id((PurpleMediaCodec *)codec);
621 gchar *
622 sipe_backend_codec_get_name(struct sipe_backend_codec *codec)
624 /* Not explicitly documented, but return value must be g_free()'d */
625 return purple_media_codec_get_encoding_name((PurpleMediaCodec *)codec);
628 guint
629 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
631 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
634 void
635 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec,
636 const gchar *name, const gchar *value)
638 purple_media_codec_add_optional_parameter((PurpleMediaCodec *)codec, name, value);
641 GList *
642 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
644 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
647 gboolean
648 sipe_backend_set_remote_codecs(struct sipe_backend_media *media,
649 struct sipe_backend_stream *stream,
650 GList *codecs)
652 return purple_media_set_remote_codecs(media->m,
653 stream->sessionid,
654 stream->participant,
655 codecs);
658 GList*
659 sipe_backend_get_local_codecs(struct sipe_backend_media *media,
660 struct sipe_backend_stream *stream)
662 GList *codecs = purple_media_get_codecs(media->m,
663 stream->sessionid);
664 GList *i = codecs;
665 gboolean is_conference = (g_strstr_len(stream->participant,
666 strlen(stream->participant),
667 "app:conf:audio-video:") != NULL);
670 * Do not announce Theora. Its optional parameters are too long,
671 * Communicator rejects such SDP message and does not support the codec
672 * anyway.
674 * For some yet unknown reason, A/V conferencing server does not accept
675 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
676 * we are able to decode incoming SIREN from server and with MSOC
677 * client, bidirectional call using the codec works. Until resolved,
678 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
679 * seems to work properly in this scenario.
681 while (i) {
682 PurpleMediaCodec *codec = i->data;
683 gchar *encoding_name = purple_media_codec_get_encoding_name(codec);
685 if (sipe_strequal(encoding_name,"THEORA") ||
686 (is_conference && sipe_strequal(encoding_name,"SIREN"))) {
687 GList *tmp;
688 g_object_unref(codec);
689 tmp = i->next;
690 codecs = g_list_delete_link(codecs, i);
691 i = tmp;
692 } else
693 i = i->next;
695 g_free(encoding_name);
698 return codecs;
701 struct sipe_backend_candidate *
702 sipe_backend_candidate_new(const gchar *foundation,
703 SipeComponentType component,
704 SipeCandidateType type, SipeNetworkProtocol proto,
705 const gchar *ip, guint port,
706 const gchar *username,
707 const gchar *password)
709 PurpleMediaCandidate *c = purple_media_candidate_new(
710 /* Libnice and Farsight rely on non-NULL foundation to
711 * distinguish between candidates of a component. When NULL
712 * foundation is passed (ie. ICE draft 6 does not use foudation),
713 * use username instead. If no foundation is provided, Farsight
714 * may signal an active candidate different from the one actually
715 * in use. See Farsight's agent_new_selected_pair() in
716 * fs-nice-stream-transmitter.h where first candidate in the
717 * remote list is always selected when no foundation. */
718 foundation ? foundation : username,
719 component,
720 sipe_candidate_type_to_purple(type),
721 sipe_network_protocol_to_purple(proto),
723 port);
724 g_object_set(c, "username", username, "password", password, NULL);
725 return (struct sipe_backend_candidate *)c;
728 void
729 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
731 if (candidate)
732 g_object_unref(candidate);
735 gchar *
736 sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate)
738 /* Not explicitly documented, but return value must be g_free()'d */
739 return purple_media_candidate_get_username((PurpleMediaCandidate*)candidate);
742 gchar *
743 sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate)
745 /* Not explicitly documented, but return value must be g_free()'d */
746 return purple_media_candidate_get_password((PurpleMediaCandidate*)candidate);
749 gchar *
750 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate)
752 /* Not explicitly documented, but return value must be g_free()'d */
753 return purple_media_candidate_get_foundation((PurpleMediaCandidate*)candidate);
756 gchar *
757 sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate)
759 /* Not explicitly documented, but return value must be g_free()'d */
760 return purple_media_candidate_get_ip((PurpleMediaCandidate*)candidate);
763 guint
764 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
766 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
769 gchar *
770 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate)
772 /* Not explicitly documented, but return value must be g_free()'d */
773 return purple_media_candidate_get_base_ip((PurpleMediaCandidate*)candidate);
776 guint
777 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
779 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
782 guint32
783 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
785 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
788 void
789 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
791 g_object_set(candidate, "priority", priority, NULL);
794 SipeComponentType
795 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
797 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
800 SipeCandidateType
801 sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate)
803 PurpleMediaCandidateType type =
804 purple_media_candidate_get_candidate_type((PurpleMediaCandidate*)candidate);
805 return purple_candidate_type_to_sipe(type);
808 SipeNetworkProtocol
809 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate)
811 PurpleMediaNetworkProtocol proto =
812 purple_media_candidate_get_protocol((PurpleMediaCandidate*)candidate);
813 return purple_network_protocol_to_sipe(proto);
816 static void
817 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
818 gpointer value,
819 gpointer user_data)
821 GList *entry = value;
822 GList **candidates = user_data;
824 g_object_unref(entry->data);
825 *candidates = g_list_delete_link(*candidates, entry);
828 static GList *
829 ensure_candidate_pairs(GList *candidates)
831 GHashTable *lone_cand_links;
832 GList *i;
834 lone_cand_links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
836 for (i = candidates; i; i = i->next) {
837 PurpleMediaCandidate *c = i->data;
838 gchar *foundation = purple_media_candidate_get_foundation(c);
840 if (g_hash_table_lookup(lone_cand_links, foundation)) {
841 g_hash_table_remove(lone_cand_links, foundation);
842 g_free(foundation);
843 } else {
844 g_hash_table_insert(lone_cand_links, foundation, i);
848 g_hash_table_foreach(lone_cand_links, remove_lone_candidate_cb, &candidates);
849 g_hash_table_destroy(lone_cand_links);
851 return candidates;
854 GList *
855 sipe_backend_get_local_candidates(struct sipe_backend_media *media,
856 struct sipe_backend_stream *stream)
858 GList *candidates = purple_media_get_local_candidates(media->m,
859 stream->sessionid,
860 stream->participant);
862 * Sometimes purple will not return complete list of candidates, even
863 * after "candidates-prepared" signal is emitted. This is a feature of
864 * libnice, namely affecting candidates discovered via UPnP. Nice does
865 * not wait until discovery is finished and can signal end of candidate
866 * gathering before all responses from UPnP enabled gateways are received.
868 * Remove any incomplete RTP+RTCP candidate pairs from the list.
870 candidates = ensure_candidate_pairs(candidates);
871 return candidates;
874 void
875 sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local)
877 if (media)
878 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT,
879 NULL, NULL, local);
882 void
883 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
885 if (media)
886 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
887 NULL, NULL, local);
890 void
891 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
893 if (media)
894 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
895 NULL, NULL, local);
898 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
900 switch (type) {
901 case SIPE_MEDIA_AUDIO: return PURPLE_MEDIA_AUDIO;
902 case SIPE_MEDIA_VIDEO: return PURPLE_MEDIA_VIDEO;
903 default: return PURPLE_MEDIA_NONE;
907 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
909 switch (type) {
910 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
911 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
912 default: return SIPE_MEDIA_AUDIO;
916 static PurpleMediaCandidateType
917 sipe_candidate_type_to_purple(SipeCandidateType type)
919 switch (type) {
920 case SIPE_CANDIDATE_TYPE_HOST: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
921 case SIPE_CANDIDATE_TYPE_RELAY: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
922 case SIPE_CANDIDATE_TYPE_SRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
923 case SIPE_CANDIDATE_TYPE_PRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX;
924 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
928 static SipeCandidateType
929 purple_candidate_type_to_sipe(PurpleMediaCandidateType type)
931 switch (type) {
932 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: return SIPE_CANDIDATE_TYPE_HOST;
933 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: return SIPE_CANDIDATE_TYPE_RELAY;
934 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: return SIPE_CANDIDATE_TYPE_SRFLX;
935 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: return SIPE_CANDIDATE_TYPE_PRFLX;
936 default: return SIPE_CANDIDATE_TYPE_HOST;
940 static PurpleMediaNetworkProtocol
941 sipe_network_protocol_to_purple(SipeNetworkProtocol proto)
943 switch (proto) {
944 #ifdef HAVE_ICE_TCP
945 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
946 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE;
947 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
948 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE;
949 #else
950 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
951 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
952 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP;
953 #endif
954 case SIPE_NETWORK_PROTOCOL_UDP:
955 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
956 default:
957 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
961 static SipeNetworkProtocol
962 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto)
964 switch (proto) {
965 #ifdef HAVE_ICE_TCP
966 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE:
967 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
968 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE:
969 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
970 #else
971 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP:
972 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
973 #endif
974 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
975 return SIPE_NETWORK_PROTOCOL_UDP;
976 default:
977 return SIPE_NETWORK_PROTOCOL_UDP;
982 Local Variables:
983 mode: c
984 c-file-style: "bsd"
985 indent-tabs-mode: t
986 tab-width: 8
987 End: