purple: disable deprecated warnings for GValueArray
[siplcs.git] / src / purple / purple-media.c
blobdf91569f65976ca30c95b65ae3ec1f05fb8657b9
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 "media-gst.h"
37 #include "agent.h"
39 #include "sipe-backend.h"
40 #include "sipe-core.h"
42 #include "purple-private.h"
45 * GValueArray has been marked deprecated in glib 2.32 but it is still used by
46 * libpurple and libfarsight APIs. Therefore we need to disable the deprecated
47 * warning so that the code still compiles for platforms that enable it.
49 * Diagnostic #pragma was added in GCC 4.2.0
51 #if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 2)
52 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
53 #endif
55 struct sipe_backend_media {
56 PurpleMedia *m;
57 GSList *streams;
58 /**
59 * Number of media streams that were not yet locally accepted or rejected.
61 guint unconfirmed_streams;
64 struct sipe_backend_stream {
65 gchar *sessionid;
66 gchar *participant;
67 gboolean candidates_prepared;
68 gboolean local_on_hold;
69 gboolean remote_on_hold;
70 gboolean accepted;
73 static void
74 backend_stream_free(struct sipe_backend_stream *stream)
76 if (stream) {
77 g_free(stream->sessionid);
78 g_free(stream->participant);
79 g_free(stream);
83 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type);
84 static PurpleMediaCandidateType sipe_candidate_type_to_purple(SipeCandidateType type);
85 static SipeCandidateType purple_candidate_type_to_sipe(PurpleMediaCandidateType type);
86 static PurpleMediaNetworkProtocol sipe_network_protocol_to_purple(SipeNetworkProtocol proto);
87 static SipeNetworkProtocol purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto);
89 static void
90 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
91 gchar *sessionid,
92 SIPE_UNUSED_PARAMETER gchar *participant,
93 struct sipe_media_call *call)
95 struct sipe_backend_stream *stream;
96 stream = sipe_backend_media_get_stream_by_id(call->backend_private, sessionid);
98 stream->candidates_prepared = TRUE;
100 if (call->candidates_prepared_cb &&
101 sipe_backend_candidates_prepared(call->backend_private)) {
102 call->candidates_prepared_cb(call, stream);
106 static void
107 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
108 PurpleMediaState state,
109 gchar *sessionid,
110 gchar *participant,
111 struct sipe_media_call *call)
113 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state, sessionid, participant);
114 if (state == PURPLE_MEDIA_STATE_END &&
115 !sessionid && !participant && call->media_end_cb)
116 call->media_end_cb(call);
119 /* Used externally in purple-plugin.c. This declaration stops the compiler
120 * complaining about missing prototype. */
121 void capture_pipeline(gchar *label);
123 void
124 capture_pipeline(gchar *label) {
125 PurpleMediaManager *manager = purple_media_manager_get();
126 GstElement *pipeline = purple_media_manager_get_pipeline(manager);
127 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, label);
130 static void
131 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *message,
132 struct sipe_media_call *call)
134 capture_pipeline("ERROR");
136 if (call->error_cb)
137 call->error_cb(call, message);
140 static void
141 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
142 PurpleMediaInfoType type,
143 gchar *sessionid,
144 gchar *participant,
145 gboolean local,
146 struct sipe_media_call *call)
148 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
149 if (call->call_accept_cb && !sessionid && !participant)
150 call->call_accept_cb(call, local);
151 else if (sessionid && participant) {
152 struct sipe_backend_stream *stream;
153 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
154 sessionid);
155 if (stream) {
156 if (!stream->accepted && local)
157 --call->backend_private->unconfirmed_streams;
158 stream->accepted = TRUE;
161 } else if (type == PURPLE_MEDIA_INFO_HOLD || type == PURPLE_MEDIA_INFO_UNHOLD) {
163 gboolean state = (type == PURPLE_MEDIA_INFO_HOLD);
165 if (sessionid) {
166 // Hold specific stream
167 struct sipe_backend_stream *stream;
168 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
169 sessionid);
171 if (local)
172 stream->local_on_hold = state;
173 else
174 stream->remote_on_hold = state;
175 } else {
176 // Hold all streams
177 GSList *i = sipe_backend_media_get_streams(call->backend_private);
178 for (; i; i = i->next) {
179 struct sipe_backend_stream *stream = i->data;
181 if (local)
182 stream->local_on_hold = state;
183 else
184 stream->remote_on_hold = state;
188 if (call->call_hold_cb)
189 call->call_hold_cb(call, local, state);
190 } else if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) {
191 if (!sessionid && !participant) {
192 if (type == PURPLE_MEDIA_INFO_HANGUP && call->call_hangup_cb)
193 call->call_hangup_cb(call, local);
194 else if (type == PURPLE_MEDIA_INFO_REJECT && call->call_reject_cb && !local)
195 call->call_reject_cb(call, local);
196 } else if (sessionid && participant) {
197 struct sipe_backend_stream *stream;
198 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
199 sessionid);
201 if (stream) {
202 call->backend_private->streams = g_slist_remove(call->backend_private->streams, stream);
203 backend_stream_free(stream);
204 if (local && --call->backend_private->unconfirmed_streams == 0 &&
205 call->call_reject_cb)
206 call->call_reject_cb(call, local);
212 struct sipe_backend_media *
213 sipe_backend_media_new(struct sipe_core_public *sipe_public,
214 struct sipe_media_call *call,
215 const gchar *participant,
216 gboolean initiator)
218 struct sipe_backend_media *media = g_new0(struct sipe_backend_media, 1);
219 struct sipe_backend_private *purple_private = sipe_public->backend_private;
220 PurpleMediaManager *manager = purple_media_manager_get();
221 GstElement *pipeline;
223 media->m = purple_media_manager_create_media(manager,
224 purple_private->account,
225 "fsrtpconference",
226 participant, initiator);
228 g_signal_connect(G_OBJECT(media->m), "candidates-prepared",
229 G_CALLBACK(on_candidates_prepared_cb), call);
230 g_signal_connect(G_OBJECT(media->m), "stream-info",
231 G_CALLBACK(on_stream_info_cb), call);
232 g_signal_connect(G_OBJECT(media->m), "error",
233 G_CALLBACK(on_error_cb), call);
234 g_signal_connect(G_OBJECT(media->m), "state-changed",
235 G_CALLBACK(on_state_changed_cb), call);
237 /* On error, the pipeline is no longer in PLAYING state and libpurple
238 * will not switch it back to PLAYING, preventing any more calls until
239 * application restart. We switch the state ourselves here to negate
240 * effect of any error in previous call (if any). */
241 pipeline = purple_media_manager_get_pipeline(manager);
242 gst_element_set_state(pipeline, GST_STATE_PLAYING);
244 return media;
247 void
248 sipe_backend_media_free(struct sipe_backend_media *media)
250 if (media) {
251 GSList *stream = media->streams;
253 for (; stream; stream = g_slist_delete_link(stream, stream))
254 backend_stream_free(stream->data);
256 g_free(media);
260 void
261 sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname)
263 if (media) {
264 guint num_params = 3;
265 GParameter *params = g_new0(GParameter, num_params);
266 params[0].name = "sdes-cname";
267 g_value_init(&params[0].value, G_TYPE_STRING);
268 g_value_set_string(&params[0].value, cname);
269 params[1].name = "sdes-name";
270 g_value_init(&params[1].value, G_TYPE_STRING);
271 params[2].name = "sdes-tool";
272 g_value_init(&params[2].value, G_TYPE_STRING);
274 purple_media_set_params(media->m, num_params, params);
276 g_value_unset(&params[0].value);
277 g_free(params);
281 #define FS_CODECS_CONF \
282 "# Automatically created by SIPE plugin\n" \
283 "[video/H263]\n" \
284 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
285 "\n" \
286 "[audio/PCMA]\n" \
287 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
288 "\n" \
289 "[audio/PCMU]\n" \
290 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
292 static void
293 ensure_codecs_conf()
295 gchar *filename;
296 filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
298 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
299 int fd = g_open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
300 gchar *fs_codecs_conf = FS_CODECS_CONF;
301 if ((fd < 0) || write(fd, fs_codecs_conf, strlen(fs_codecs_conf)) == -1)
302 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
303 if (fd >= 0)
304 close(fd);
307 g_free(filename);
310 static void
311 append_relay(GValueArray *relay_info, const gchar *ip, guint port, gchar *type,
312 gchar *username, gchar *password)
314 GValue value;
315 GstStructure *gst_relay_info;
317 gst_relay_info = gst_structure_new("relay-info",
318 "ip", G_TYPE_STRING, ip,
319 "port", G_TYPE_UINT, port,
320 "relay-type", G_TYPE_STRING, type,
321 "username", G_TYPE_STRING, username,
322 "password", G_TYPE_STRING, password,
323 NULL);
325 if (gst_relay_info) {
326 memset(&value, 0, sizeof(GValue));
327 g_value_init(&value, GST_TYPE_STRUCTURE);
328 gst_value_set_structure(&value, gst_relay_info);
330 g_value_array_append(relay_info, &value);
331 gst_structure_free(gst_relay_info);
335 struct sipe_backend_media_relays *
336 sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password)
338 GValueArray *relay_info = g_value_array_new(0);
340 for (; media_relays; media_relays = media_relays->next) {\
341 struct sipe_media_relay *relay = media_relays->data;
343 /* Skip relays where IP could not be resolved. */
344 if (!relay->hostname)
345 continue;
347 if (relay->udp_port != 0)
348 append_relay(relay_info, relay->hostname, relay->udp_port,
349 "udp", username, password);
351 #ifdef HAVE_ICE_TCP
352 if (relay->tcp_port != 0)
353 append_relay(relay_info, relay->hostname, relay->tcp_port,
354 "tcp", username, password);
355 #endif
358 return (struct sipe_backend_media_relays *)relay_info;
361 void
362 sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays)
364 g_value_array_free((GValueArray *)media_relays);
367 static guint
368 stream_demultiplex_cb(const gchar *buf, SIPE_UNUSED_PARAMETER gpointer *user_data)
370 guint8 payload_type = buf[1] & 0x7F;
371 if (payload_type >= 200 && payload_type <=204) {
372 // Looks like RTCP
373 return PURPLE_MEDIA_COMPONENT_RTCP;
374 } else {
375 // Looks like RTP
376 return PURPLE_MEDIA_COMPONENT_RTP;
380 struct sipe_backend_stream *
381 sipe_backend_media_add_stream(struct sipe_backend_media *media,
382 const gchar *id,
383 const gchar *participant,
384 SipeMediaType type,
385 SipeIceVersion ice_version,
386 gboolean initiator,
387 struct sipe_backend_media_relays *media_relays)
389 struct sipe_backend_stream *stream = NULL;
390 PurpleMediaSessionType prpl_type = sipe_media_to_purple(type);
391 GParameter *params = NULL;
392 guint params_cnt = 0;
393 gchar *transmitter;
395 if (ice_version != SIPE_ICE_NO_ICE) {
396 transmitter = "nice";
397 params_cnt = 4;
399 params = g_new0(GParameter, params_cnt);
401 params[0].name = "compatibility-mode";
402 g_value_init(&params[0].value, G_TYPE_UINT);
403 g_value_set_uint(&params[0].value,
404 ice_version == SIPE_ICE_DRAFT_6 ?
405 NICE_COMPATIBILITY_OC2007 :
406 NICE_COMPATIBILITY_OC2007R2);
408 params[1].name = "transport-protocols";
409 g_value_init(&params[1].value, G_TYPE_UINT);
410 #ifdef HAVE_ICE_TCP
411 g_value_set_uint(&params[1].value,
412 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP |
413 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE |
414 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE);
415 #else
416 g_value_set_uint(&params[1].value,
417 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP);
418 #endif
420 params[2].name = "demultiplex-func";
421 g_value_init(&params[2].value, G_TYPE_POINTER);
422 g_value_set_pointer(&params[2].value, stream_demultiplex_cb);
424 if (media_relays) {
425 params[3].name = "relay-info";
426 g_value_init(&params[3].value, G_TYPE_VALUE_ARRAY);
427 g_value_set_boxed(&params[3].value, media_relays);
428 } else
429 --params_cnt;
430 } else {
431 // TODO: session naming here, Communicator needs audio/video
432 transmitter = "rawudp";
433 //sessionid = "sipe-voice-rawudp";
436 ensure_codecs_conf();
438 if (purple_media_add_stream(media->m, id, participant, prpl_type,
439 initiator, transmitter, params_cnt,
440 params)) {
441 stream = g_new0(struct sipe_backend_stream, 1);
442 stream->sessionid = g_strdup(id);
443 stream->participant = g_strdup(participant);
444 stream->candidates_prepared = FALSE;
446 media->streams = g_slist_append(media->streams, stream);
447 if (!initiator)
448 ++media->unconfirmed_streams;
451 if (params && media_relays)
452 g_value_unset(&params[3].value);
454 g_free(params);
456 return stream;
459 void
460 sipe_backend_media_remove_stream(struct sipe_backend_media *media,
461 struct sipe_backend_stream *stream)
463 g_return_if_fail(media && stream);
465 purple_media_end(media->m, stream->sessionid, NULL);
466 media->streams = g_slist_remove(media->streams, stream);
467 backend_stream_free(stream);
470 GSList *sipe_backend_media_get_streams(struct sipe_backend_media *media)
472 return media->streams;
475 struct sipe_backend_stream *
476 sipe_backend_media_get_stream_by_id(struct sipe_backend_media *media,
477 const gchar *id)
479 GSList *i;
480 for (i = media->streams; i; i = i->next) {
481 struct sipe_backend_stream *stream = i->data;
482 if (sipe_strequal(stream->sessionid, id))
483 return stream;
485 return NULL;
488 void
489 sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
490 struct sipe_backend_stream *stream,
491 GList *candidates)
493 GList *udp_candidates = NULL;
495 #ifndef HAVE_ICE_TCP
496 while (candidates) {
497 PurpleMediaCandidate *candidate = candidates->data;
498 PurpleMediaNetworkProtocol proto;
500 proto = purple_media_candidate_get_protocol(candidate);
501 if (proto == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP)
502 udp_candidates = g_list_append(udp_candidates, candidate);
504 candidates = candidates->next;
507 candidates = udp_candidates;
508 #endif
511 purple_media_add_remote_candidates(media->m, stream->sessionid,
512 stream->participant, candidates);
514 g_list_free(udp_candidates);
517 gboolean sipe_backend_media_is_initiator(struct sipe_backend_media *media,
518 struct sipe_backend_stream *stream)
520 return purple_media_is_initiator(media->m,
521 stream ? stream->sessionid : NULL,
522 stream ? stream->participant : NULL);
525 gboolean sipe_backend_media_accepted(struct sipe_backend_media *media)
527 return purple_media_accepted(media->m, NULL, NULL);
530 gboolean
531 sipe_backend_candidates_prepared(struct sipe_backend_media *media)
533 GSList *streams = media->streams;
534 for (; streams; streams = streams->next) {
535 struct sipe_backend_stream *s = streams->data;
536 if (!s->candidates_prepared)
537 return FALSE;
539 return TRUE;
542 GList *
543 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media *media,
544 struct sipe_backend_stream *stream)
546 return purple_media_get_active_local_candidates(media->m,
547 stream->sessionid,
548 stream->participant);
551 GList *
552 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media *media,
553 struct sipe_backend_stream *stream)
555 return purple_media_get_active_remote_candidates(media->m,
556 stream->sessionid,
557 stream->participant);
560 gchar *
561 sipe_backend_stream_get_id(struct sipe_backend_stream *stream)
563 return stream->sessionid;
566 void sipe_backend_stream_hold(struct sipe_backend_media *media,
567 struct sipe_backend_stream *stream,
568 gboolean local)
570 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HOLD,
571 stream->sessionid, stream->participant,
572 local);
575 void sipe_backend_stream_unhold(struct sipe_backend_media *media,
576 struct sipe_backend_stream *stream,
577 gboolean local)
579 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_UNHOLD,
580 stream->sessionid, stream->participant,
581 local);
584 gboolean sipe_backend_stream_is_held(struct sipe_backend_stream *stream)
586 g_return_val_if_fail(stream, FALSE);
588 return stream->local_on_hold || stream->remote_on_hold;
591 struct sipe_backend_codec *
592 sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate)
594 return (struct sipe_backend_codec *)purple_media_codec_new(id, name,
595 sipe_media_to_purple(type),
596 clock_rate);
599 void
600 sipe_backend_codec_free(struct sipe_backend_codec *codec)
602 if (codec)
603 g_object_unref(codec);
607 sipe_backend_codec_get_id(struct sipe_backend_codec *codec)
609 return purple_media_codec_get_id((PurpleMediaCodec *)codec);
612 gchar *
613 sipe_backend_codec_get_name(struct sipe_backend_codec *codec)
615 /* Not explicitly documented, but return value must be g_free()'d */
616 return purple_media_codec_get_encoding_name((PurpleMediaCodec *)codec);
619 guint
620 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
622 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
625 void
626 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec,
627 const gchar *name, const gchar *value)
629 purple_media_codec_add_optional_parameter((PurpleMediaCodec *)codec, name, value);
632 GList *
633 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
635 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
638 gboolean
639 sipe_backend_set_remote_codecs(struct sipe_backend_media *media,
640 struct sipe_backend_stream *stream,
641 GList *codecs)
643 return purple_media_set_remote_codecs(media->m,
644 stream->sessionid,
645 stream->participant,
646 codecs);
649 GList*
650 sipe_backend_get_local_codecs(struct sipe_backend_media *media,
651 struct sipe_backend_stream *stream)
653 GList *codecs = purple_media_get_codecs(media->m,
654 stream->sessionid);
655 GList *i = codecs;
656 gboolean is_conference = (g_strstr_len(stream->participant,
657 strlen(stream->participant),
658 "app:conf:audio-video:") != NULL);
661 * Do not announce Theora. Its optional parameters are too long,
662 * Communicator rejects such SDP message and does not support the codec
663 * anyway.
665 * For some yet unknown reason, A/V conferencing server does not accept
666 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
667 * we are able to decode incoming SIREN from server and with MSOC
668 * client, bidirectional call using the codec works. Until resolved,
669 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
670 * seems to work properly in this scenario.
672 while (i) {
673 PurpleMediaCodec *codec = i->data;
674 gchar *encoding_name = purple_media_codec_get_encoding_name(codec);
676 if (sipe_strequal(encoding_name,"THEORA") ||
677 (is_conference && sipe_strequal(encoding_name,"SIREN"))) {
678 GList *tmp;
679 g_object_unref(codec);
680 tmp = i->next;
681 codecs = g_list_delete_link(codecs, i);
682 i = tmp;
683 } else
684 i = i->next;
686 g_free(encoding_name);
689 return codecs;
692 struct sipe_backend_candidate *
693 sipe_backend_candidate_new(const gchar *foundation,
694 SipeComponentType component,
695 SipeCandidateType type, SipeNetworkProtocol proto,
696 const gchar *ip, guint port,
697 const gchar *username,
698 const gchar *password)
700 PurpleMediaCandidate *c = purple_media_candidate_new(
701 /* Libnice and Farsight rely on non-NULL foundation to
702 * distinguish between candidates of a component. When NULL
703 * foundation is passed (ie. ICE draft 6 does not use foudation),
704 * use username instead. If no foundation is provided, Farsight
705 * may signal an active candidate different from the one actually
706 * in use. See Farsight's agent_new_selected_pair() in
707 * fs-nice-stream-transmitter.h where first candidate in the
708 * remote list is always selected when no foundation. */
709 foundation ? foundation : username,
710 component,
711 sipe_candidate_type_to_purple(type),
712 sipe_network_protocol_to_purple(proto),
714 port);
715 g_object_set(c, "username", username, "password", password, NULL);
716 return (struct sipe_backend_candidate *)c;
719 void
720 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
722 if (candidate)
723 g_object_unref(candidate);
726 gchar *
727 sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate)
729 /* Not explicitly documented, but return value must be g_free()'d */
730 return purple_media_candidate_get_username((PurpleMediaCandidate*)candidate);
733 gchar *
734 sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate)
736 /* Not explicitly documented, but return value must be g_free()'d */
737 return purple_media_candidate_get_password((PurpleMediaCandidate*)candidate);
740 gchar *
741 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate)
743 /* Not explicitly documented, but return value must be g_free()'d */
744 return purple_media_candidate_get_foundation((PurpleMediaCandidate*)candidate);
747 gchar *
748 sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate)
750 /* Not explicitly documented, but return value must be g_free()'d */
751 return purple_media_candidate_get_ip((PurpleMediaCandidate*)candidate);
754 guint
755 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
757 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
760 gchar *
761 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate)
763 /* Not explicitly documented, but return value must be g_free()'d */
764 return purple_media_candidate_get_base_ip((PurpleMediaCandidate*)candidate);
767 guint
768 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
770 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
773 guint32
774 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
776 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
779 void
780 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
782 g_object_set(candidate, "priority", priority, NULL);
785 SipeComponentType
786 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
788 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
791 SipeCandidateType
792 sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate)
794 PurpleMediaCandidateType type =
795 purple_media_candidate_get_candidate_type((PurpleMediaCandidate*)candidate);
796 return purple_candidate_type_to_sipe(type);
799 SipeNetworkProtocol
800 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate)
802 PurpleMediaNetworkProtocol proto =
803 purple_media_candidate_get_protocol((PurpleMediaCandidate*)candidate);
804 return purple_network_protocol_to_sipe(proto);
807 static void
808 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
809 gpointer value,
810 gpointer user_data)
812 GList *entry = value;
813 GList **candidates = user_data;
815 g_object_unref(entry->data);
816 *candidates = g_list_delete_link(*candidates, entry);
819 static GList *
820 ensure_candidate_pairs(GList *candidates)
822 GHashTable *lone_cand_links;
823 GList *i;
825 lone_cand_links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
827 for (i = candidates; i; i = i->next) {
828 PurpleMediaCandidate *c = i->data;
829 gchar *foundation = purple_media_candidate_get_foundation(c);
831 if (g_hash_table_lookup(lone_cand_links, foundation)) {
832 g_hash_table_remove(lone_cand_links, foundation);
833 g_free(foundation);
834 } else {
835 g_hash_table_insert(lone_cand_links, foundation, i);
839 g_hash_table_foreach(lone_cand_links, remove_lone_candidate_cb, &candidates);
840 g_hash_table_destroy(lone_cand_links);
842 return candidates;
845 GList *
846 sipe_backend_get_local_candidates(struct sipe_backend_media *media,
847 struct sipe_backend_stream *stream)
849 GList *candidates = purple_media_get_local_candidates(media->m,
850 stream->sessionid,
851 stream->participant);
853 * Sometimes purple will not return complete list of candidates, even
854 * after "candidates-prepared" signal is emitted. This is a feature of
855 * libnice, namely affecting candidates discovered via UPnP. Nice does
856 * not wait until discovery is finished and can signal end of candidate
857 * gathering before all responses from UPnP enabled gateways are received.
859 * Remove any incomplete RTP+RTCP candidate pairs from the list.
861 candidates = ensure_candidate_pairs(candidates);
862 return candidates;
865 void
866 sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local)
868 if (media)
869 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT,
870 NULL, NULL, local);
873 void
874 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
876 if (media)
877 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
878 NULL, NULL, local);
881 void
882 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
884 if (media)
885 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
886 NULL, NULL, local);
889 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
891 switch (type) {
892 case SIPE_MEDIA_AUDIO: return PURPLE_MEDIA_AUDIO;
893 case SIPE_MEDIA_VIDEO: return PURPLE_MEDIA_VIDEO;
894 default: return PURPLE_MEDIA_NONE;
898 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
900 switch (type) {
901 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
902 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
903 default: return SIPE_MEDIA_AUDIO;
907 static PurpleMediaCandidateType
908 sipe_candidate_type_to_purple(SipeCandidateType type)
910 switch (type) {
911 case SIPE_CANDIDATE_TYPE_HOST: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
912 case SIPE_CANDIDATE_TYPE_RELAY: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
913 case SIPE_CANDIDATE_TYPE_SRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
914 case SIPE_CANDIDATE_TYPE_PRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX;
915 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
919 static SipeCandidateType
920 purple_candidate_type_to_sipe(PurpleMediaCandidateType type)
922 switch (type) {
923 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: return SIPE_CANDIDATE_TYPE_HOST;
924 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: return SIPE_CANDIDATE_TYPE_RELAY;
925 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: return SIPE_CANDIDATE_TYPE_SRFLX;
926 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: return SIPE_CANDIDATE_TYPE_PRFLX;
927 default: return SIPE_CANDIDATE_TYPE_HOST;
931 static PurpleMediaNetworkProtocol
932 sipe_network_protocol_to_purple(SipeNetworkProtocol proto)
934 switch (proto) {
935 #ifdef HAVE_ICE_TCP
936 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
937 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE;
938 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
939 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE;
940 #else
941 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
942 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
943 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP;
944 #endif
945 case SIPE_NETWORK_PROTOCOL_UDP:
946 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
947 default:
948 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
952 static SipeNetworkProtocol
953 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto)
955 switch (proto) {
956 #ifdef HAVE_ICE_TCP
957 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE:
958 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
959 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE:
960 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
961 #else
962 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP:
963 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
964 #endif
965 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
966 return SIPE_NETWORK_PROTOCOL_UDP;
967 default:
968 return SIPE_NETWORK_PROTOCOL_UDP;
973 Local Variables:
974 mode: c
975 c-file-style: "bsd"
976 indent-tabs-mode: t
977 tab-width: 8
978 End: