media: Farstream 0.1.1 compatibility fix
[siplcs.git] / src / purple / purple-media.c
blobcff5d5cb36bdfa5b0ef5d02949f2a61f044b4a31
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 local_on_hold;
76 gboolean remote_on_hold;
77 gboolean accepted;
80 static void
81 backend_stream_free(struct sipe_backend_stream *stream)
83 if (stream) {
84 g_free(stream->sessionid);
85 g_free(stream->participant);
86 g_free(stream);
90 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type);
91 static PurpleMediaCandidateType sipe_candidate_type_to_purple(SipeCandidateType type);
92 static SipeCandidateType purple_candidate_type_to_sipe(PurpleMediaCandidateType type);
93 static PurpleMediaNetworkProtocol sipe_network_protocol_to_purple(SipeNetworkProtocol proto);
94 static SipeNetworkProtocol purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto);
96 static void
97 maybe_signal_stream_initialized(struct sipe_media_call *call, gchar *sessionid)
99 if (call->stream_initialized_cb) {
100 struct sipe_backend_stream *stream;
101 stream = sipe_backend_media_get_stream_by_id(call->backend_private, sessionid);
103 if (sipe_backend_stream_initialized(call->backend_private, stream)) {
104 call->stream_initialized_cb(call, stream);
109 static void
110 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
111 gchar *sessionid,
112 SIPE_UNUSED_PARAMETER gchar *participant,
113 struct sipe_media_call *call)
115 maybe_signal_stream_initialized(call, sessionid);
118 static void
119 on_codecs_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
120 gchar *sessionid,
121 struct sipe_media_call *call)
123 maybe_signal_stream_initialized(call, sessionid);
126 static void
127 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
128 PurpleMediaState state,
129 gchar *sessionid,
130 gchar *participant,
131 struct sipe_media_call *call)
133 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state, sessionid, participant);
134 if (state == PURPLE_MEDIA_STATE_END &&
135 !sessionid && !participant && call->media_end_cb)
136 call->media_end_cb(call);
139 void
140 capture_pipeline(const gchar *label) {
141 PurpleMediaManager *manager = purple_media_manager_get();
142 GstElement *pipeline = purple_media_manager_get_pipeline(manager);
143 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, label);
146 static void
147 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *message,
148 struct sipe_media_call *call)
150 capture_pipeline("ERROR");
152 if (call->error_cb)
153 call->error_cb(call, message);
156 static void
157 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
158 PurpleMediaInfoType type,
159 gchar *sessionid,
160 gchar *participant,
161 gboolean local,
162 struct sipe_media_call *call)
164 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
165 if (call->call_accept_cb && !sessionid && !participant)
166 call->call_accept_cb(call, local);
167 else if (sessionid && participant) {
168 struct sipe_backend_stream *stream;
169 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
170 sessionid);
171 if (stream) {
172 if (!stream->accepted && local)
173 --call->backend_private->unconfirmed_streams;
174 stream->accepted = TRUE;
177 } else if (type == PURPLE_MEDIA_INFO_HOLD || type == PURPLE_MEDIA_INFO_UNHOLD) {
179 gboolean state = (type == PURPLE_MEDIA_INFO_HOLD);
181 if (sessionid) {
182 // Hold specific stream
183 struct sipe_backend_stream *stream;
184 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
185 sessionid);
187 if (local)
188 stream->local_on_hold = state;
189 else
190 stream->remote_on_hold = state;
191 } else {
192 // Hold all streams
193 GSList *i = sipe_backend_media_get_streams(call->backend_private);
194 for (; i; i = i->next) {
195 struct sipe_backend_stream *stream = i->data;
197 if (local)
198 stream->local_on_hold = state;
199 else
200 stream->remote_on_hold = state;
204 if (call->call_hold_cb)
205 call->call_hold_cb(call, local, state);
206 } else if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) {
207 if (!sessionid && !participant) {
208 if (type == PURPLE_MEDIA_INFO_HANGUP && call->call_hangup_cb)
209 call->call_hangup_cb(call, local);
210 else if (type == PURPLE_MEDIA_INFO_REJECT && call->call_reject_cb && !local)
211 call->call_reject_cb(call, local);
212 } else if (sessionid && participant) {
213 struct sipe_backend_stream *stream;
214 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
215 sessionid);
217 if (stream) {
218 call->backend_private->streams = g_slist_remove(call->backend_private->streams, stream);
219 backend_stream_free(stream);
220 if (local && --call->backend_private->unconfirmed_streams == 0 &&
221 call->call_reject_cb)
222 call->call_reject_cb(call, local);
228 struct sipe_backend_media *
229 sipe_backend_media_new(struct sipe_core_public *sipe_public,
230 struct sipe_media_call *call,
231 const gchar *participant,
232 gboolean initiator)
234 struct sipe_backend_media *media = g_new0(struct sipe_backend_media, 1);
235 struct sipe_backend_private *purple_private = sipe_public->backend_private;
236 PurpleMediaManager *manager = purple_media_manager_get();
237 GstElement *pipeline;
239 media->m = purple_media_manager_create_media(manager,
240 purple_private->account,
241 "fsrtpconference",
242 participant, initiator);
244 g_signal_connect(G_OBJECT(media->m), "candidates-prepared",
245 G_CALLBACK(on_candidates_prepared_cb), call);
246 g_signal_connect(G_OBJECT(media->m), "codecs-changed",
247 G_CALLBACK(on_codecs_changed_cb), call);
248 g_signal_connect(G_OBJECT(media->m), "stream-info",
249 G_CALLBACK(on_stream_info_cb), call);
250 g_signal_connect(G_OBJECT(media->m), "error",
251 G_CALLBACK(on_error_cb), call);
252 g_signal_connect(G_OBJECT(media->m), "state-changed",
253 G_CALLBACK(on_state_changed_cb), call);
255 /* On error, the pipeline is no longer in PLAYING state and libpurple
256 * will not switch it back to PLAYING, preventing any more calls until
257 * application restart. We switch the state ourselves here to negate
258 * effect of any error in previous call (if any). */
259 pipeline = purple_media_manager_get_pipeline(manager);
260 gst_element_set_state(pipeline, GST_STATE_PLAYING);
262 return media;
265 void
266 sipe_backend_media_free(struct sipe_backend_media *media)
268 if (media) {
269 GSList *stream = media->streams;
271 for (; stream; stream = g_slist_delete_link(stream, stream))
272 backend_stream_free(stream->data);
274 g_free(media);
278 void
279 sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname)
281 if (media) {
282 guint num_params = 3;
283 GParameter *params = g_new0(GParameter, num_params);
284 params[0].name = "sdes-cname";
285 g_value_init(&params[0].value, G_TYPE_STRING);
286 g_value_set_string(&params[0].value, cname);
287 params[1].name = "sdes-name";
288 g_value_init(&params[1].value, G_TYPE_STRING);
289 params[2].name = "sdes-tool";
290 g_value_init(&params[2].value, G_TYPE_STRING);
292 purple_media_set_params(media->m, num_params, params);
294 g_value_unset(&params[0].value);
295 g_free(params);
299 #define FS_CODECS_CONF \
300 "# Automatically created by SIPE plugin\n" \
301 "[video/H263]\n" \
302 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
303 "\n" \
304 "[audio/PCMA]\n" \
305 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
306 "\n" \
307 "[audio/PCMU]\n" \
308 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
310 static void
311 ensure_codecs_conf()
313 gchar *filename;
314 filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
316 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
317 int fd = g_open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
318 gchar *fs_codecs_conf = FS_CODECS_CONF;
319 if ((fd < 0) || write(fd, fs_codecs_conf, strlen(fs_codecs_conf)) == -1)
320 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
321 if (fd >= 0)
322 close(fd);
325 g_free(filename);
328 static void
329 append_relay(GValueArray *relay_info, const gchar *ip, guint port, gchar *type,
330 gchar *username, gchar *password)
332 GValue value;
333 GstStructure *gst_relay_info;
335 gst_relay_info = gst_structure_new("relay-info",
336 "ip", G_TYPE_STRING, ip,
337 "port", G_TYPE_UINT, port,
338 "relay-type", G_TYPE_STRING, type,
339 "username", G_TYPE_STRING, username,
340 "password", G_TYPE_STRING, password,
341 NULL);
343 if (gst_relay_info) {
344 memset(&value, 0, sizeof(GValue));
345 g_value_init(&value, GST_TYPE_STRUCTURE);
346 gst_value_set_structure(&value, gst_relay_info);
348 g_value_array_append(relay_info, &value);
349 gst_structure_free(gst_relay_info);
353 struct sipe_backend_media_relays *
354 sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password)
356 GValueArray *relay_info = g_value_array_new(0);
358 for (; media_relays; media_relays = media_relays->next) {\
359 struct sipe_media_relay *relay = media_relays->data;
361 /* Skip relays where IP could not be resolved. */
362 if (!relay->hostname)
363 continue;
365 if (relay->udp_port != 0)
366 append_relay(relay_info, relay->hostname, relay->udp_port,
367 "udp", username, password);
369 #ifdef HAVE_ICE_TCP
370 if (relay->tcp_port != 0)
371 append_relay(relay_info, relay->hostname, relay->tcp_port,
372 "tcp", username, password);
373 #endif
376 return (struct sipe_backend_media_relays *)relay_info;
379 void
380 sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays)
382 g_value_array_free((GValueArray *)media_relays);
385 static guint
386 stream_demultiplex_cb(const gchar *buf, SIPE_UNUSED_PARAMETER gpointer *user_data)
388 guint8 payload_type = buf[1] & 0x7F;
389 if (payload_type >= 200 && payload_type <=204) {
390 // Looks like RTCP
391 return PURPLE_MEDIA_COMPONENT_RTCP;
392 } else {
393 // Looks like RTP
394 return PURPLE_MEDIA_COMPONENT_RTP;
398 struct sipe_backend_stream *
399 sipe_backend_media_add_stream(struct sipe_backend_media *media,
400 const gchar *id,
401 const gchar *participant,
402 SipeMediaType type,
403 SipeIceVersion ice_version,
404 gboolean initiator,
405 struct sipe_backend_media_relays *media_relays)
407 struct sipe_backend_stream *stream = NULL;
408 PurpleMediaSessionType prpl_type = sipe_media_to_purple(type);
409 GParameter *params = NULL;
410 guint params_cnt = 0;
411 gchar *transmitter;
413 if (ice_version != SIPE_ICE_NO_ICE) {
414 transmitter = "nice";
415 params_cnt = 4;
417 params = g_new0(GParameter, params_cnt);
419 params[0].name = "compatibility-mode";
420 g_value_init(&params[0].value, G_TYPE_UINT);
421 g_value_set_uint(&params[0].value,
422 ice_version == SIPE_ICE_DRAFT_6 ?
423 NICE_COMPATIBILITY_OC2007 :
424 NICE_COMPATIBILITY_OC2007R2);
426 params[1].name = "transport-protocols";
427 g_value_init(&params[1].value, G_TYPE_UINT);
428 #ifdef HAVE_ICE_TCP
429 g_value_set_uint(&params[1].value,
430 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP |
431 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE |
432 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE);
433 #else
434 g_value_set_uint(&params[1].value,
435 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP);
436 #endif
438 params[2].name = "demultiplex-func";
439 g_value_init(&params[2].value, G_TYPE_POINTER);
440 g_value_set_pointer(&params[2].value, stream_demultiplex_cb);
442 if (media_relays) {
443 params[3].name = "relay-info";
444 g_value_init(&params[3].value, G_TYPE_VALUE_ARRAY);
445 g_value_set_boxed(&params[3].value, media_relays);
446 } else
447 --params_cnt;
448 } else {
449 // TODO: session naming here, Communicator needs audio/video
450 transmitter = "rawudp";
451 //sessionid = "sipe-voice-rawudp";
453 /* To avoid Coverity FORWARD_NULL warning. params_cnt is
454 * still 0 so this is a no-op. libpurple API documentation
455 * doesn't specify if params can be NULL or not. */
456 params = g_new0(GParameter, 1);
459 ensure_codecs_conf();
461 if (purple_media_add_stream(media->m, id, participant, prpl_type,
462 initiator, transmitter, params_cnt,
463 params)) {
464 stream = g_new0(struct sipe_backend_stream, 1);
465 stream->sessionid = g_strdup(id);
466 stream->participant = g_strdup(participant);
468 media->streams = g_slist_append(media->streams, stream);
469 if (!initiator)
470 ++media->unconfirmed_streams;
473 if ((params_cnt > 2) && media_relays)
474 g_value_unset(&params[3].value);
476 g_free(params);
478 return stream;
481 void
482 sipe_backend_media_remove_stream(struct sipe_backend_media *media,
483 struct sipe_backend_stream *stream)
485 g_return_if_fail(media && stream);
487 purple_media_end(media->m, stream->sessionid, NULL);
488 media->streams = g_slist_remove(media->streams, stream);
489 backend_stream_free(stream);
492 GSList *sipe_backend_media_get_streams(struct sipe_backend_media *media)
494 return media->streams;
497 struct sipe_backend_stream *
498 sipe_backend_media_get_stream_by_id(struct sipe_backend_media *media,
499 const gchar *id)
501 GSList *i;
502 for (i = media->streams; i; i = i->next) {
503 struct sipe_backend_stream *stream = i->data;
504 if (sipe_strequal(stream->sessionid, id))
505 return stream;
507 return NULL;
510 void
511 sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
512 struct sipe_backend_stream *stream,
513 GList *candidates)
515 GList *udp_candidates = NULL;
517 #ifndef HAVE_ICE_TCP
518 while (candidates) {
519 PurpleMediaCandidate *candidate = candidates->data;
520 PurpleMediaNetworkProtocol proto;
522 proto = purple_media_candidate_get_protocol(candidate);
523 if (proto == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP)
524 udp_candidates = g_list_append(udp_candidates, candidate);
526 candidates = candidates->next;
529 candidates = udp_candidates;
530 #endif
533 purple_media_add_remote_candidates(media->m, stream->sessionid,
534 stream->participant, candidates);
536 g_list_free(udp_candidates);
539 gboolean sipe_backend_media_is_initiator(struct sipe_backend_media *media,
540 struct sipe_backend_stream *stream)
542 return purple_media_is_initiator(media->m,
543 stream ? stream->sessionid : NULL,
544 stream ? stream->participant : NULL);
547 gboolean sipe_backend_media_accepted(struct sipe_backend_media *media)
549 return purple_media_accepted(media->m, NULL, NULL);
552 gboolean
553 sipe_backend_stream_initialized(struct sipe_backend_media *media,
554 struct sipe_backend_stream *stream)
556 g_return_val_if_fail(media, FALSE);
557 g_return_val_if_fail(stream, FALSE);
559 if (purple_media_candidates_prepared(media->m,
560 stream->sessionid,
561 stream->participant)) {
562 GList *codecs;
563 codecs = purple_media_get_codecs(media->m, stream->sessionid);
564 if (codecs) {
565 purple_media_codec_list_free(codecs);
566 return TRUE;
569 return FALSE;
572 GList *
573 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media *media,
574 struct sipe_backend_stream *stream)
576 return purple_media_get_active_local_candidates(media->m,
577 stream->sessionid,
578 stream->participant);
581 GList *
582 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media *media,
583 struct sipe_backend_stream *stream)
585 return purple_media_get_active_remote_candidates(media->m,
586 stream->sessionid,
587 stream->participant);
590 const gchar *
591 sipe_backend_stream_get_id(struct sipe_backend_stream *stream)
593 return stream->sessionid;
596 void sipe_backend_stream_hold(struct sipe_backend_media *media,
597 struct sipe_backend_stream *stream,
598 gboolean local)
600 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HOLD,
601 stream->sessionid, stream->participant,
602 local);
605 void sipe_backend_stream_unhold(struct sipe_backend_media *media,
606 struct sipe_backend_stream *stream,
607 gboolean local)
609 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_UNHOLD,
610 stream->sessionid, stream->participant,
611 local);
614 gboolean sipe_backend_stream_is_held(struct sipe_backend_stream *stream)
616 g_return_val_if_fail(stream, FALSE);
618 return stream->local_on_hold || stream->remote_on_hold;
621 struct sipe_backend_codec *
622 sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate)
624 return (struct sipe_backend_codec *)purple_media_codec_new(id, name,
625 sipe_media_to_purple(type),
626 clock_rate);
629 void
630 sipe_backend_codec_free(struct sipe_backend_codec *codec)
632 if (codec)
633 g_object_unref(codec);
637 sipe_backend_codec_get_id(struct sipe_backend_codec *codec)
639 return purple_media_codec_get_id((PurpleMediaCodec *)codec);
642 gchar *
643 sipe_backend_codec_get_name(struct sipe_backend_codec *codec)
645 /* Not explicitly documented, but return value must be g_free()'d */
646 return purple_media_codec_get_encoding_name((PurpleMediaCodec *)codec);
649 guint
650 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
652 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
655 void
656 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec,
657 const gchar *name, const gchar *value)
659 purple_media_codec_add_optional_parameter((PurpleMediaCodec *)codec, name, value);
662 GList *
663 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
665 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
668 gboolean
669 sipe_backend_set_remote_codecs(struct sipe_backend_media *media,
670 struct sipe_backend_stream *stream,
671 GList *codecs)
673 return purple_media_set_remote_codecs(media->m,
674 stream->sessionid,
675 stream->participant,
676 codecs);
679 GList*
680 sipe_backend_get_local_codecs(struct sipe_backend_media *media,
681 struct sipe_backend_stream *stream)
683 GList *codecs = purple_media_get_codecs(media->m,
684 stream->sessionid);
685 GList *i = codecs;
686 gboolean is_conference = (g_strstr_len(stream->participant,
687 strlen(stream->participant),
688 "app:conf:audio-video:") != NULL);
691 * Do not announce Theora. Its optional parameters are too long,
692 * Communicator rejects such SDP message and does not support the codec
693 * anyway.
695 * For some yet unknown reason, A/V conferencing server does not accept
696 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
697 * we are able to decode incoming SIREN from server and with MSOC
698 * client, bidirectional call using the codec works. Until resolved,
699 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
700 * seems to work properly in this scenario.
702 while (i) {
703 PurpleMediaCodec *codec = i->data;
704 gchar *encoding_name = purple_media_codec_get_encoding_name(codec);
706 if (sipe_strequal(encoding_name,"THEORA") ||
707 (is_conference && sipe_strequal(encoding_name,"SIREN"))) {
708 GList *tmp;
709 g_object_unref(codec);
710 tmp = i->next;
711 codecs = g_list_delete_link(codecs, i);
712 i = tmp;
713 } else
714 i = i->next;
716 g_free(encoding_name);
719 return codecs;
722 struct sipe_backend_candidate *
723 sipe_backend_candidate_new(const gchar *foundation,
724 SipeComponentType component,
725 SipeCandidateType type, SipeNetworkProtocol proto,
726 const gchar *ip, guint port,
727 const gchar *username,
728 const gchar *password)
730 PurpleMediaCandidate *c = purple_media_candidate_new(
731 /* Libnice and Farsight rely on non-NULL foundation to
732 * distinguish between candidates of a component. When NULL
733 * foundation is passed (ie. ICE draft 6 does not use foudation),
734 * use username instead. If no foundation is provided, Farsight
735 * may signal an active candidate different from the one actually
736 * in use. See Farsight's agent_new_selected_pair() in
737 * fs-nice-stream-transmitter.h where first candidate in the
738 * remote list is always selected when no foundation. */
739 foundation ? foundation : username,
740 component,
741 sipe_candidate_type_to_purple(type),
742 sipe_network_protocol_to_purple(proto),
744 port);
745 g_object_set(c, "username", username, "password", password, NULL);
746 return (struct sipe_backend_candidate *)c;
749 void
750 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
752 if (candidate)
753 g_object_unref(candidate);
756 gchar *
757 sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate)
759 /* Not explicitly documented, but return value must be g_free()'d */
760 return purple_media_candidate_get_username((PurpleMediaCandidate*)candidate);
763 gchar *
764 sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate)
766 /* Not explicitly documented, but return value must be g_free()'d */
767 return purple_media_candidate_get_password((PurpleMediaCandidate*)candidate);
770 gchar *
771 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate)
773 /* Not explicitly documented, but return value must be g_free()'d */
774 return purple_media_candidate_get_foundation((PurpleMediaCandidate*)candidate);
777 gchar *
778 sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate)
780 /* Not explicitly documented, but return value must be g_free()'d */
781 return purple_media_candidate_get_ip((PurpleMediaCandidate*)candidate);
784 guint
785 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
787 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
790 gchar *
791 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate)
793 /* Not explicitly documented, but return value must be g_free()'d */
794 return purple_media_candidate_get_base_ip((PurpleMediaCandidate*)candidate);
797 guint
798 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
800 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
803 guint32
804 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
806 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
809 void
810 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
812 g_object_set(candidate, "priority", priority, NULL);
815 SipeComponentType
816 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
818 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
821 SipeCandidateType
822 sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate)
824 PurpleMediaCandidateType type =
825 purple_media_candidate_get_candidate_type((PurpleMediaCandidate*)candidate);
826 return purple_candidate_type_to_sipe(type);
829 SipeNetworkProtocol
830 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate)
832 PurpleMediaNetworkProtocol proto =
833 purple_media_candidate_get_protocol((PurpleMediaCandidate*)candidate);
834 return purple_network_protocol_to_sipe(proto);
837 static void
838 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
839 gpointer value,
840 gpointer user_data)
842 GList *entry = value;
843 GList **candidates = user_data;
845 g_object_unref(entry->data);
846 *candidates = g_list_delete_link(*candidates, entry);
849 static GList *
850 ensure_candidate_pairs(GList *candidates)
852 GHashTable *lone_cand_links;
853 GList *i;
855 lone_cand_links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
857 for (i = candidates; i; i = i->next) {
858 PurpleMediaCandidate *c = i->data;
859 gchar *foundation = purple_media_candidate_get_foundation(c);
861 if (g_hash_table_lookup(lone_cand_links, foundation)) {
862 g_hash_table_remove(lone_cand_links, foundation);
863 g_free(foundation);
864 } else {
865 g_hash_table_insert(lone_cand_links, foundation, i);
869 g_hash_table_foreach(lone_cand_links, remove_lone_candidate_cb, &candidates);
870 g_hash_table_destroy(lone_cand_links);
872 return candidates;
875 GList *
876 sipe_backend_get_local_candidates(struct sipe_backend_media *media,
877 struct sipe_backend_stream *stream)
879 GList *candidates = purple_media_get_local_candidates(media->m,
880 stream->sessionid,
881 stream->participant);
883 * Sometimes purple will not return complete list of candidates, even
884 * after "candidates-prepared" signal is emitted. This is a feature of
885 * libnice, namely affecting candidates discovered via UPnP. Nice does
886 * not wait until discovery is finished and can signal end of candidate
887 * gathering before all responses from UPnP enabled gateways are received.
889 * Remove any incomplete RTP+RTCP candidate pairs from the list.
891 candidates = ensure_candidate_pairs(candidates);
892 return candidates;
895 void
896 sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local)
898 if (media)
899 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT,
900 NULL, NULL, local);
903 void
904 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
906 if (media)
907 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
908 NULL, NULL, local);
911 void
912 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
914 if (media)
915 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
916 NULL, NULL, local);
919 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
921 switch (type) {
922 case SIPE_MEDIA_AUDIO: return PURPLE_MEDIA_AUDIO;
923 case SIPE_MEDIA_VIDEO: return PURPLE_MEDIA_VIDEO;
924 default: return PURPLE_MEDIA_NONE;
928 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
930 switch (type) {
931 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
932 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
933 default: return SIPE_MEDIA_AUDIO;
937 static PurpleMediaCandidateType
938 sipe_candidate_type_to_purple(SipeCandidateType type)
940 switch (type) {
941 case SIPE_CANDIDATE_TYPE_HOST: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
942 case SIPE_CANDIDATE_TYPE_RELAY: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
943 case SIPE_CANDIDATE_TYPE_SRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
944 case SIPE_CANDIDATE_TYPE_PRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX;
945 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
949 static SipeCandidateType
950 purple_candidate_type_to_sipe(PurpleMediaCandidateType type)
952 switch (type) {
953 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: return SIPE_CANDIDATE_TYPE_HOST;
954 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: return SIPE_CANDIDATE_TYPE_RELAY;
955 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: return SIPE_CANDIDATE_TYPE_SRFLX;
956 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: return SIPE_CANDIDATE_TYPE_PRFLX;
957 default: return SIPE_CANDIDATE_TYPE_HOST;
961 static PurpleMediaNetworkProtocol
962 sipe_network_protocol_to_purple(SipeNetworkProtocol proto)
964 switch (proto) {
965 #ifdef HAVE_ICE_TCP
966 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
967 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE;
968 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
969 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE;
970 #else
971 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
972 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
973 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP;
974 #endif
975 case SIPE_NETWORK_PROTOCOL_UDP:
976 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
977 default:
978 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
982 static SipeNetworkProtocol
983 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto)
985 switch (proto) {
986 #ifdef HAVE_ICE_TCP
987 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE:
988 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
989 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE:
990 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
991 #else
992 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP:
993 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
994 #endif
995 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
996 return SIPE_NETWORK_PROTOCOL_UDP;
997 default:
998 return SIPE_NETWORK_PROTOCOL_UDP;
1003 Local Variables:
1004 mode: c
1005 c-file-style: "bsd"
1006 indent-tabs-mode: t
1007 tab-width: 8
1008 End: