purple: replace deprecated glib2 functions
[siplcs.git] / src / purple / purple-media.c
blob54fea9be4dbc2b9aad0aab8489809231ed1f1b00
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"
44 struct sipe_backend_media {
45 PurpleMedia *m;
46 GSList *streams;
47 /**
48 * Number of media streams that were not yet locally accepted or rejected.
50 guint unconfirmed_streams;
53 struct sipe_backend_stream {
54 gchar *sessionid;
55 gchar *participant;
56 gboolean candidates_prepared;
57 gboolean local_on_hold;
58 gboolean remote_on_hold;
59 gboolean accepted;
62 static void
63 backend_stream_free(struct sipe_backend_stream *stream)
65 if (stream) {
66 g_free(stream->sessionid);
67 g_free(stream->participant);
68 g_free(stream);
72 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type);
73 static PurpleMediaCandidateType sipe_candidate_type_to_purple(SipeCandidateType type);
74 static SipeCandidateType purple_candidate_type_to_sipe(PurpleMediaCandidateType type);
75 static PurpleMediaNetworkProtocol sipe_network_protocol_to_purple(SipeNetworkProtocol proto);
76 static SipeNetworkProtocol purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto);
78 static void
79 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
80 gchar *sessionid,
81 SIPE_UNUSED_PARAMETER gchar *participant,
82 struct sipe_media_call *call)
84 struct sipe_backend_stream *stream;
85 stream = sipe_backend_media_get_stream_by_id(call->backend_private, sessionid);
87 stream->candidates_prepared = TRUE;
89 if (call->candidates_prepared_cb &&
90 sipe_backend_candidates_prepared(call->backend_private)) {
91 call->candidates_prepared_cb(call, stream);
95 static void
96 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
97 PurpleMediaState state,
98 gchar *sessionid,
99 gchar *participant,
100 struct sipe_media_call *call)
102 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state, sessionid, participant);
103 if (state == PURPLE_MEDIA_STATE_END &&
104 !sessionid && !participant && call->media_end_cb)
105 call->media_end_cb(call);
108 /* Used externally in purple-plugin.c. This declaration stops the compiler
109 * complaining about missing prototype. */
110 void capture_pipeline(gchar *label);
112 void
113 capture_pipeline(gchar *label) {
114 PurpleMediaManager *manager = purple_media_manager_get();
115 GstElement *pipeline = purple_media_manager_get_pipeline(manager);
116 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, label);
119 static void
120 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *message,
121 struct sipe_media_call *call)
123 capture_pipeline("ERROR");
125 if (call->error_cb)
126 call->error_cb(call, message);
129 static void
130 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
131 PurpleMediaInfoType type,
132 gchar *sessionid,
133 gchar *participant,
134 gboolean local,
135 struct sipe_media_call *call)
137 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
138 if (call->call_accept_cb && !sessionid && !participant)
139 call->call_accept_cb(call, local);
140 else if (sessionid && participant) {
141 struct sipe_backend_stream *stream;
142 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
143 sessionid);
144 if (stream) {
145 if (!stream->accepted && local)
146 --call->backend_private->unconfirmed_streams;
147 stream->accepted = TRUE;
150 } else if (type == PURPLE_MEDIA_INFO_HOLD || type == PURPLE_MEDIA_INFO_UNHOLD) {
152 gboolean state = (type == PURPLE_MEDIA_INFO_HOLD);
154 if (sessionid) {
155 // Hold specific stream
156 struct sipe_backend_stream *stream;
157 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
158 sessionid);
160 if (local)
161 stream->local_on_hold = state;
162 else
163 stream->remote_on_hold = state;
164 } else {
165 // Hold all streams
166 GSList *i = sipe_backend_media_get_streams(call->backend_private);
167 for (; i; i = i->next) {
168 struct sipe_backend_stream *stream = i->data;
170 if (local)
171 stream->local_on_hold = state;
172 else
173 stream->remote_on_hold = state;
177 if (call->call_hold_cb)
178 call->call_hold_cb(call, local, state);
179 } else if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) {
180 if (!sessionid && !participant) {
181 if (type == PURPLE_MEDIA_INFO_HANGUP && call->call_hangup_cb)
182 call->call_hangup_cb(call, local);
183 else if (type == PURPLE_MEDIA_INFO_REJECT && call->call_reject_cb && !local)
184 call->call_reject_cb(call, local);
185 } else if (sessionid && participant) {
186 struct sipe_backend_stream *stream;
187 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
188 sessionid);
190 if (stream) {
191 call->backend_private->streams = g_slist_remove(call->backend_private->streams, stream);
192 backend_stream_free(stream);
193 if (local && --call->backend_private->unconfirmed_streams == 0 &&
194 call->call_reject_cb)
195 call->call_reject_cb(call, local);
201 struct sipe_backend_media *
202 sipe_backend_media_new(struct sipe_core_public *sipe_public,
203 struct sipe_media_call *call,
204 const gchar *participant,
205 gboolean initiator)
207 struct sipe_backend_media *media = g_new0(struct sipe_backend_media, 1);
208 struct sipe_backend_private *purple_private = sipe_public->backend_private;
209 PurpleMediaManager *manager = purple_media_manager_get();
210 GstElement *pipeline;
212 media->m = purple_media_manager_create_media(manager,
213 purple_private->account,
214 "fsrtpconference",
215 participant, initiator);
217 g_signal_connect(G_OBJECT(media->m), "candidates-prepared",
218 G_CALLBACK(on_candidates_prepared_cb), call);
219 g_signal_connect(G_OBJECT(media->m), "stream-info",
220 G_CALLBACK(on_stream_info_cb), call);
221 g_signal_connect(G_OBJECT(media->m), "error",
222 G_CALLBACK(on_error_cb), call);
223 g_signal_connect(G_OBJECT(media->m), "state-changed",
224 G_CALLBACK(on_state_changed_cb), call);
226 /* On error, the pipeline is no longer in PLAYING state and libpurple
227 * will not switch it back to PLAYING, preventing any more calls until
228 * application restart. We switch the state ourselves here to negate
229 * effect of any error in previous call (if any). */
230 pipeline = purple_media_manager_get_pipeline(manager);
231 gst_element_set_state(pipeline, GST_STATE_PLAYING);
233 return media;
236 void
237 sipe_backend_media_free(struct sipe_backend_media *media)
239 if (media) {
240 GSList *stream = media->streams;
242 for (; stream; stream = g_slist_delete_link(stream, stream))
243 backend_stream_free(stream->data);
245 g_free(media);
249 void
250 sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname)
252 if (media) {
253 guint num_params = 3;
254 GParameter *params = g_new0(GParameter, num_params);
255 params[0].name = "sdes-cname";
256 g_value_init(&params[0].value, G_TYPE_STRING);
257 g_value_set_string(&params[0].value, cname);
258 params[1].name = "sdes-name";
259 g_value_init(&params[1].value, G_TYPE_STRING);
260 params[2].name = "sdes-tool";
261 g_value_init(&params[2].value, G_TYPE_STRING);
263 purple_media_set_params(media->m, num_params, params);
265 g_value_unset(&params[0].value);
266 g_free(params);
270 #define FS_CODECS_CONF \
271 "# Automatically created by SIPE plugin\n" \
272 "[video/H263]\n" \
273 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
274 "\n" \
275 "[audio/PCMA]\n" \
276 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
277 "\n" \
278 "[audio/PCMU]\n" \
279 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
281 static void
282 ensure_codecs_conf()
284 gchar *filename;
285 filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
287 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
288 int fd = g_open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
289 gchar *fs_codecs_conf = FS_CODECS_CONF;
290 if ((fd < 0) || write(fd, fs_codecs_conf, strlen(fs_codecs_conf)) == -1)
291 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
292 if (fd >= 0)
293 close(fd);
296 g_free(filename);
299 static void
300 append_relay(GArray *relay_info, const gchar *ip, guint port, gchar *type,
301 gchar *username, gchar *password)
303 GValue value;
304 GstStructure *gst_relay_info;
306 gst_relay_info = gst_structure_new("relay-info",
307 "ip", G_TYPE_STRING, ip,
308 "port", G_TYPE_UINT, port,
309 "relay-type", G_TYPE_STRING, type,
310 "username", G_TYPE_STRING, username,
311 "password", G_TYPE_STRING, password,
312 NULL);
314 if (gst_relay_info) {
315 memset(&value, 0, sizeof(GValue));
316 g_value_init(&value, GST_TYPE_STRUCTURE);
317 gst_value_set_structure(&value, gst_relay_info);
319 g_array_append_val(relay_info, value);
320 gst_structure_free(gst_relay_info);
324 struct sipe_backend_media_relays *
325 sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password)
327 GArray *relay_info = g_array_new(FALSE, FALSE, sizeof(GValue));
329 for (; media_relays; media_relays = media_relays->next) {
330 struct sipe_media_relay *relay = media_relays->data;
332 /* Skip relays where IP could not be resolved. */
333 if (!relay->hostname)
334 continue;
336 if (relay->udp_port != 0)
337 append_relay(relay_info, relay->hostname, relay->udp_port,
338 "udp", username, password);
340 #ifdef HAVE_ICE_TCP
341 if (relay->tcp_port != 0)
342 append_relay(relay_info, relay->hostname, relay->tcp_port,
343 "tcp", username, password);
344 #endif
347 return (struct sipe_backend_media_relays *)relay_info;
350 void
351 sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays)
353 GArray *relay_info = (GArray *) media_relays;
354 guint i;
355 for (i = 0; i < relay_info->len; i++) {
356 GValue *value = &g_array_index(relay_info, GValue, i);
357 g_value_unset(value);
359 g_array_free(relay_info, TRUE);
362 static guint
363 stream_demultiplex_cb(const gchar *buf, SIPE_UNUSED_PARAMETER gpointer *user_data)
365 guint8 payload_type = buf[1] & 0x7F;
366 if (payload_type >= 200 && payload_type <=204) {
367 // Looks like RTCP
368 return PURPLE_MEDIA_COMPONENT_RTCP;
369 } else {
370 // Looks like RTP
371 return PURPLE_MEDIA_COMPONENT_RTP;
375 struct sipe_backend_stream *
376 sipe_backend_media_add_stream(struct sipe_backend_media *media,
377 const gchar *id,
378 const gchar *participant,
379 SipeMediaType type,
380 SipeIceVersion ice_version,
381 gboolean initiator,
382 struct sipe_backend_media_relays *media_relays)
384 struct sipe_backend_stream *stream = NULL;
385 PurpleMediaSessionType prpl_type = sipe_media_to_purple(type);
386 GParameter *params = NULL;
387 guint params_cnt = 0;
388 gchar *transmitter;
390 if (ice_version != SIPE_ICE_NO_ICE) {
391 transmitter = "nice";
392 params_cnt = 4;
394 params = g_new0(GParameter, params_cnt);
396 params[0].name = "compatibility-mode";
397 g_value_init(&params[0].value, G_TYPE_UINT);
398 g_value_set_uint(&params[0].value,
399 ice_version == SIPE_ICE_DRAFT_6 ?
400 NICE_COMPATIBILITY_OC2007 :
401 NICE_COMPATIBILITY_OC2007R2);
403 params[1].name = "transport-protocols";
404 g_value_init(&params[1].value, G_TYPE_UINT);
405 #ifdef HAVE_ICE_TCP
406 g_value_set_uint(&params[1].value,
407 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP |
408 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE |
409 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE);
410 #else
411 g_value_set_uint(&params[1].value,
412 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP);
413 #endif
415 params[2].name = "demultiplex-func";
416 g_value_init(&params[2].value, G_TYPE_POINTER);
417 g_value_set_pointer(&params[2].value, stream_demultiplex_cb);
419 if (media_relays) {
420 params[3].name = "relay-info";
421 g_value_init(&params[3].value, G_TYPE_ARRAY);
422 g_value_set_boxed(&params[3].value, media_relays);
423 } else
424 --params_cnt;
425 } else {
426 // TODO: session naming here, Communicator needs audio/video
427 transmitter = "rawudp";
428 //sessionid = "sipe-voice-rawudp";
431 ensure_codecs_conf();
433 if (purple_media_add_stream(media->m, id, participant, prpl_type,
434 initiator, transmitter, params_cnt,
435 params)) {
436 stream = g_new0(struct sipe_backend_stream, 1);
437 stream->sessionid = g_strdup(id);
438 stream->participant = g_strdup(participant);
439 stream->candidates_prepared = FALSE;
441 media->streams = g_slist_append(media->streams, stream);
442 if (!initiator)
443 ++media->unconfirmed_streams;
446 if (params && media_relays)
447 g_value_unset(&params[3].value);
449 g_free(params);
451 return stream;
454 void
455 sipe_backend_media_remove_stream(struct sipe_backend_media *media,
456 struct sipe_backend_stream *stream)
458 g_return_if_fail(media && stream);
460 purple_media_end(media->m, stream->sessionid, NULL);
461 media->streams = g_slist_remove(media->streams, stream);
462 backend_stream_free(stream);
465 GSList *sipe_backend_media_get_streams(struct sipe_backend_media *media)
467 return media->streams;
470 struct sipe_backend_stream *
471 sipe_backend_media_get_stream_by_id(struct sipe_backend_media *media,
472 const gchar *id)
474 GSList *i;
475 for (i = media->streams; i; i = i->next) {
476 struct sipe_backend_stream *stream = i->data;
477 if (sipe_strequal(stream->sessionid, id))
478 return stream;
480 return NULL;
483 void
484 sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
485 struct sipe_backend_stream *stream,
486 GList *candidates)
488 GList *udp_candidates = NULL;
490 #ifndef HAVE_ICE_TCP
491 while (candidates) {
492 PurpleMediaCandidate *candidate = candidates->data;
493 PurpleMediaNetworkProtocol proto;
495 proto = purple_media_candidate_get_protocol(candidate);
496 if (proto == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP)
497 udp_candidates = g_list_append(udp_candidates, candidate);
499 candidates = candidates->next;
502 candidates = udp_candidates;
503 #endif
506 purple_media_add_remote_candidates(media->m, stream->sessionid,
507 stream->participant, candidates);
509 g_list_free(udp_candidates);
512 gboolean sipe_backend_media_is_initiator(struct sipe_backend_media *media,
513 struct sipe_backend_stream *stream)
515 return purple_media_is_initiator(media->m,
516 stream ? stream->sessionid : NULL,
517 stream ? stream->participant : NULL);
520 gboolean sipe_backend_media_accepted(struct sipe_backend_media *media)
522 return purple_media_accepted(media->m, NULL, NULL);
525 gboolean
526 sipe_backend_candidates_prepared(struct sipe_backend_media *media)
528 GSList *streams = media->streams;
529 for (; streams; streams = streams->next) {
530 struct sipe_backend_stream *s = streams->data;
531 if (!s->candidates_prepared)
532 return FALSE;
534 return TRUE;
537 GList *
538 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media *media,
539 struct sipe_backend_stream *stream)
541 return purple_media_get_active_local_candidates(media->m,
542 stream->sessionid,
543 stream->participant);
546 GList *
547 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media *media,
548 struct sipe_backend_stream *stream)
550 return purple_media_get_active_remote_candidates(media->m,
551 stream->sessionid,
552 stream->participant);
555 gchar *
556 sipe_backend_stream_get_id(struct sipe_backend_stream *stream)
558 return stream->sessionid;
561 void sipe_backend_stream_hold(struct sipe_backend_media *media,
562 struct sipe_backend_stream *stream,
563 gboolean local)
565 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HOLD,
566 stream->sessionid, stream->participant,
567 local);
570 void sipe_backend_stream_unhold(struct sipe_backend_media *media,
571 struct sipe_backend_stream *stream,
572 gboolean local)
574 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_UNHOLD,
575 stream->sessionid, stream->participant,
576 local);
579 gboolean sipe_backend_stream_is_held(struct sipe_backend_stream *stream)
581 g_return_val_if_fail(stream, FALSE);
583 return stream->local_on_hold || stream->remote_on_hold;
586 struct sipe_backend_codec *
587 sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate)
589 return (struct sipe_backend_codec *)purple_media_codec_new(id, name,
590 sipe_media_to_purple(type),
591 clock_rate);
594 void
595 sipe_backend_codec_free(struct sipe_backend_codec *codec)
597 if (codec)
598 g_object_unref(codec);
602 sipe_backend_codec_get_id(struct sipe_backend_codec *codec)
604 return purple_media_codec_get_id((PurpleMediaCodec *)codec);
607 gchar *
608 sipe_backend_codec_get_name(struct sipe_backend_codec *codec)
610 /* Not explicitly documented, but return value must be g_free()'d */
611 return purple_media_codec_get_encoding_name((PurpleMediaCodec *)codec);
614 guint
615 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
617 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
620 void
621 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec,
622 const gchar *name, const gchar *value)
624 purple_media_codec_add_optional_parameter((PurpleMediaCodec *)codec, name, value);
627 GList *
628 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
630 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
633 gboolean
634 sipe_backend_set_remote_codecs(struct sipe_backend_media *media,
635 struct sipe_backend_stream *stream,
636 GList *codecs)
638 return purple_media_set_remote_codecs(media->m,
639 stream->sessionid,
640 stream->participant,
641 codecs);
644 GList*
645 sipe_backend_get_local_codecs(struct sipe_backend_media *media,
646 struct sipe_backend_stream *stream)
648 GList *codecs = purple_media_get_codecs(media->m,
649 stream->sessionid);
650 GList *i = codecs;
651 gboolean is_conference = (g_strstr_len(stream->participant,
652 strlen(stream->participant),
653 "app:conf:audio-video:") != NULL);
656 * Do not announce Theora. Its optional parameters are too long,
657 * Communicator rejects such SDP message and does not support the codec
658 * anyway.
660 * For some yet unknown reason, A/V conferencing server does not accept
661 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
662 * we are able to decode incoming SIREN from server and with MSOC
663 * client, bidirectional call using the codec works. Until resolved,
664 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
665 * seems to work properly in this scenario.
667 while (i) {
668 PurpleMediaCodec *codec = i->data;
669 gchar *encoding_name = purple_media_codec_get_encoding_name(codec);
671 if (sipe_strequal(encoding_name,"THEORA") ||
672 (is_conference && sipe_strequal(encoding_name,"SIREN"))) {
673 GList *tmp;
674 g_object_unref(codec);
675 tmp = i->next;
676 codecs = g_list_delete_link(codecs, i);
677 i = tmp;
678 } else
679 i = i->next;
681 g_free(encoding_name);
684 return codecs;
687 struct sipe_backend_candidate *
688 sipe_backend_candidate_new(const gchar *foundation,
689 SipeComponentType component,
690 SipeCandidateType type, SipeNetworkProtocol proto,
691 const gchar *ip, guint port,
692 const gchar *username,
693 const gchar *password)
695 PurpleMediaCandidate *c = purple_media_candidate_new(
696 /* Libnice and Farsight rely on non-NULL foundation to
697 * distinguish between candidates of a component. When NULL
698 * foundation is passed (ie. ICE draft 6 does not use foudation),
699 * use username instead. If no foundation is provided, Farsight
700 * may signal an active candidate different from the one actually
701 * in use. See Farsight's agent_new_selected_pair() in
702 * fs-nice-stream-transmitter.h where first candidate in the
703 * remote list is always selected when no foundation. */
704 foundation ? foundation : username,
705 component,
706 sipe_candidate_type_to_purple(type),
707 sipe_network_protocol_to_purple(proto),
709 port);
710 g_object_set(c, "username", username, "password", password, NULL);
711 return (struct sipe_backend_candidate *)c;
714 void
715 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
717 if (candidate)
718 g_object_unref(candidate);
721 gchar *
722 sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate)
724 /* Not explicitly documented, but return value must be g_free()'d */
725 return purple_media_candidate_get_username((PurpleMediaCandidate*)candidate);
728 gchar *
729 sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate)
731 /* Not explicitly documented, but return value must be g_free()'d */
732 return purple_media_candidate_get_password((PurpleMediaCandidate*)candidate);
735 gchar *
736 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate)
738 /* Not explicitly documented, but return value must be g_free()'d */
739 return purple_media_candidate_get_foundation((PurpleMediaCandidate*)candidate);
742 gchar *
743 sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate)
745 /* Not explicitly documented, but return value must be g_free()'d */
746 return purple_media_candidate_get_ip((PurpleMediaCandidate*)candidate);
749 guint
750 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
752 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
755 gchar *
756 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate)
758 /* Not explicitly documented, but return value must be g_free()'d */
759 return purple_media_candidate_get_base_ip((PurpleMediaCandidate*)candidate);
762 guint
763 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
765 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
768 guint32
769 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
771 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
774 void
775 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
777 g_object_set(candidate, "priority", priority, NULL);
780 SipeComponentType
781 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
783 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
786 SipeCandidateType
787 sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate)
789 PurpleMediaCandidateType type =
790 purple_media_candidate_get_candidate_type((PurpleMediaCandidate*)candidate);
791 return purple_candidate_type_to_sipe(type);
794 SipeNetworkProtocol
795 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate)
797 PurpleMediaNetworkProtocol proto =
798 purple_media_candidate_get_protocol((PurpleMediaCandidate*)candidate);
799 return purple_network_protocol_to_sipe(proto);
802 static void
803 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
804 gpointer value,
805 gpointer user_data)
807 GList *entry = value;
808 GList **candidates = user_data;
810 g_object_unref(entry->data);
811 *candidates = g_list_delete_link(*candidates, entry);
814 static GList *
815 ensure_candidate_pairs(GList *candidates)
817 GHashTable *lone_cand_links;
818 GList *i;
820 lone_cand_links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
822 for (i = candidates; i; i = i->next) {
823 PurpleMediaCandidate *c = i->data;
824 gchar *foundation = purple_media_candidate_get_foundation(c);
826 if (g_hash_table_lookup(lone_cand_links, foundation)) {
827 g_hash_table_remove(lone_cand_links, foundation);
828 g_free(foundation);
829 } else {
830 g_hash_table_insert(lone_cand_links, foundation, i);
834 g_hash_table_foreach(lone_cand_links, remove_lone_candidate_cb, &candidates);
835 g_hash_table_destroy(lone_cand_links);
837 return candidates;
840 GList *
841 sipe_backend_get_local_candidates(struct sipe_backend_media *media,
842 struct sipe_backend_stream *stream)
844 GList *candidates = purple_media_get_local_candidates(media->m,
845 stream->sessionid,
846 stream->participant);
848 * Sometimes purple will not return complete list of candidates, even
849 * after "candidates-prepared" signal is emitted. This is a feature of
850 * libnice, namely affecting candidates discovered via UPnP. Nice does
851 * not wait until discovery is finished and can signal end of candidate
852 * gathering before all responses from UPnP enabled gateways are received.
854 * Remove any incomplete RTP+RTCP candidate pairs from the list.
856 candidates = ensure_candidate_pairs(candidates);
857 return candidates;
860 void
861 sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local)
863 if (media)
864 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT,
865 NULL, NULL, local);
868 void
869 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
871 if (media)
872 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
873 NULL, NULL, local);
876 void
877 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
879 if (media)
880 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
881 NULL, NULL, local);
884 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
886 switch (type) {
887 case SIPE_MEDIA_AUDIO: return PURPLE_MEDIA_AUDIO;
888 case SIPE_MEDIA_VIDEO: return PURPLE_MEDIA_VIDEO;
889 default: return PURPLE_MEDIA_NONE;
893 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
895 switch (type) {
896 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
897 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
898 default: return SIPE_MEDIA_AUDIO;
902 static PurpleMediaCandidateType
903 sipe_candidate_type_to_purple(SipeCandidateType type)
905 switch (type) {
906 case SIPE_CANDIDATE_TYPE_HOST: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
907 case SIPE_CANDIDATE_TYPE_RELAY: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
908 case SIPE_CANDIDATE_TYPE_SRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
909 case SIPE_CANDIDATE_TYPE_PRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX;
910 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
914 static SipeCandidateType
915 purple_candidate_type_to_sipe(PurpleMediaCandidateType type)
917 switch (type) {
918 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: return SIPE_CANDIDATE_TYPE_HOST;
919 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: return SIPE_CANDIDATE_TYPE_RELAY;
920 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: return SIPE_CANDIDATE_TYPE_SRFLX;
921 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: return SIPE_CANDIDATE_TYPE_PRFLX;
922 default: return SIPE_CANDIDATE_TYPE_HOST;
926 static PurpleMediaNetworkProtocol
927 sipe_network_protocol_to_purple(SipeNetworkProtocol proto)
929 switch (proto) {
930 #ifdef HAVE_ICE_TCP
931 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
932 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE;
933 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
934 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE;
935 #else
936 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
937 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
938 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP;
939 #endif
940 case SIPE_NETWORK_PROTOCOL_UDP:
941 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
942 default:
943 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
947 static SipeNetworkProtocol
948 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto)
950 switch (proto) {
951 #ifdef HAVE_ICE_TCP
952 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE:
953 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
954 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE:
955 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
956 #else
957 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP:
958 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
959 #endif
960 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
961 return SIPE_NETWORK_PROTOCOL_UDP;
962 default:
963 return SIPE_NETWORK_PROTOCOL_UDP;
968 Local Variables:
969 mode: c
970 c-file-style: "bsd"
971 indent-tabs-mode: t
972 tab-width: 8
973 End: