coverity: fix various warnings
[siplcs.git] / src / purple / purple-media.c
blobbc1eceb2595984e4f97a24acd03388af4f8108a4
1 /**
2 * @file purple-media.c
4 * pidgin-sipe
6 * Copyright (C) 2010-11 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(GValueArray *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_value_array_append(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 GValueArray *relay_info = g_value_array_new(0);
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 g_value_array_free((GValueArray *)media_relays);
356 static guint
357 stream_demultiplex_cb(const gchar *buf, SIPE_UNUSED_PARAMETER gpointer *user_data)
359 guint8 payload_type = buf[1] & 0x7F;
360 if (payload_type >= 200 && payload_type <=204) {
361 // Looks like RTCP
362 return PURPLE_MEDIA_COMPONENT_RTCP;
363 } else {
364 // Looks like RTP
365 return PURPLE_MEDIA_COMPONENT_RTP;
369 struct sipe_backend_stream *
370 sipe_backend_media_add_stream(struct sipe_backend_media *media,
371 const gchar *id,
372 const gchar *participant,
373 SipeMediaType type,
374 SipeIceVersion ice_version,
375 gboolean initiator,
376 struct sipe_backend_media_relays *media_relays)
378 struct sipe_backend_stream *stream = NULL;
379 PurpleMediaSessionType prpl_type = sipe_media_to_purple(type);
380 GParameter *params = NULL;
381 guint params_cnt = 0;
382 gchar *transmitter;
384 if (ice_version != SIPE_ICE_NO_ICE) {
385 transmitter = "nice";
386 params_cnt = 4;
388 params = g_new0(GParameter, params_cnt);
390 params[0].name = "compatibility-mode";
391 g_value_init(&params[0].value, G_TYPE_UINT);
392 g_value_set_uint(&params[0].value,
393 ice_version == SIPE_ICE_DRAFT_6 ?
394 NICE_COMPATIBILITY_OC2007 :
395 NICE_COMPATIBILITY_OC2007R2);
397 params[1].name = "transport-protocols";
398 g_value_init(&params[1].value, G_TYPE_UINT);
399 #ifdef HAVE_ICE_TCP
400 g_value_set_uint(&params[1].value,
401 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP |
402 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE |
403 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE);
404 #else
405 g_value_set_uint(&params[1].value,
406 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP);
407 #endif
409 params[2].name = "demultiplex-func";
410 g_value_init(&params[2].value, G_TYPE_POINTER);
411 g_value_set_pointer(&params[2].value, stream_demultiplex_cb);
413 if (media_relays) {
414 params[3].name = "relay-info";
415 g_value_init(&params[3].value, G_TYPE_VALUE_ARRAY);
416 g_value_set_boxed(&params[3].value, media_relays);
417 } else
418 --params_cnt;
419 } else {
420 // TODO: session naming here, Communicator needs audio/video
421 transmitter = "rawudp";
422 //sessionid = "sipe-voice-rawudp";
425 ensure_codecs_conf();
427 if (purple_media_add_stream(media->m, id, participant, prpl_type,
428 initiator, transmitter, params_cnt,
429 params)) {
430 stream = g_new0(struct sipe_backend_stream, 1);
431 stream->sessionid = g_strdup(id);
432 stream->participant = g_strdup(participant);
433 stream->candidates_prepared = FALSE;
435 media->streams = g_slist_append(media->streams, stream);
436 if (!initiator)
437 ++media->unconfirmed_streams;
440 if (params && media_relays)
441 g_value_unset(&params[3].value);
443 g_free(params);
445 return stream;
448 void
449 sipe_backend_media_remove_stream(struct sipe_backend_media *media,
450 struct sipe_backend_stream *stream)
452 g_return_if_fail(media && stream);
454 purple_media_end(media->m, stream->sessionid, NULL);
455 media->streams = g_slist_remove(media->streams, stream);
456 backend_stream_free(stream);
459 GSList *sipe_backend_media_get_streams(struct sipe_backend_media *media)
461 return media->streams;
464 struct sipe_backend_stream *
465 sipe_backend_media_get_stream_by_id(struct sipe_backend_media *media,
466 const gchar *id)
468 GSList *i;
469 for (i = media->streams; i; i = i->next) {
470 struct sipe_backend_stream *stream = i->data;
471 if (sipe_strequal(stream->sessionid, id))
472 return stream;
474 return NULL;
477 void
478 sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
479 struct sipe_backend_stream *stream,
480 GList *candidates)
482 GList *udp_candidates = NULL;
484 #ifndef HAVE_ICE_TCP
485 while (candidates) {
486 PurpleMediaCandidate *candidate = candidates->data;
487 PurpleMediaNetworkProtocol proto;
489 proto = purple_media_candidate_get_protocol(candidate);
490 if (proto == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP)
491 udp_candidates = g_list_append(udp_candidates, candidate);
493 candidates = candidates->next;
496 candidates = udp_candidates;
497 #endif
500 purple_media_add_remote_candidates(media->m, stream->sessionid,
501 stream->participant, candidates);
503 g_list_free(udp_candidates);
506 gboolean sipe_backend_media_is_initiator(struct sipe_backend_media *media,
507 struct sipe_backend_stream *stream)
509 return purple_media_is_initiator(media->m,
510 stream ? stream->sessionid : NULL,
511 stream ? stream->participant : NULL);
514 gboolean sipe_backend_media_accepted(struct sipe_backend_media *media)
516 return purple_media_accepted(media->m, NULL, NULL);
519 gboolean
520 sipe_backend_candidates_prepared(struct sipe_backend_media *media)
522 GSList *streams = media->streams;
523 for (; streams; streams = streams->next) {
524 struct sipe_backend_stream *s = streams->data;
525 if (!s->candidates_prepared)
526 return FALSE;
528 return TRUE;
531 GList *
532 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media *media,
533 struct sipe_backend_stream *stream)
535 return purple_media_get_active_local_candidates(media->m,
536 stream->sessionid,
537 stream->participant);
540 GList *
541 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media *media,
542 struct sipe_backend_stream *stream)
544 return purple_media_get_active_remote_candidates(media->m,
545 stream->sessionid,
546 stream->participant);
549 gchar *
550 sipe_backend_stream_get_id(struct sipe_backend_stream *stream)
552 return stream->sessionid;
555 void sipe_backend_stream_hold(struct sipe_backend_media *media,
556 struct sipe_backend_stream *stream,
557 gboolean local)
559 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HOLD,
560 stream->sessionid, stream->participant,
561 local);
564 void sipe_backend_stream_unhold(struct sipe_backend_media *media,
565 struct sipe_backend_stream *stream,
566 gboolean local)
568 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_UNHOLD,
569 stream->sessionid, stream->participant,
570 local);
573 gboolean sipe_backend_stream_is_held(struct sipe_backend_stream *stream)
575 g_return_val_if_fail(stream, FALSE);
577 return stream->local_on_hold || stream->remote_on_hold;
580 struct sipe_backend_codec *
581 sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate)
583 return (struct sipe_backend_codec *)purple_media_codec_new(id, name,
584 sipe_media_to_purple(type),
585 clock_rate);
588 void
589 sipe_backend_codec_free(struct sipe_backend_codec *codec)
591 if (codec)
592 g_object_unref(codec);
596 sipe_backend_codec_get_id(struct sipe_backend_codec *codec)
598 return purple_media_codec_get_id((PurpleMediaCodec *)codec);
601 gchar *
602 sipe_backend_codec_get_name(struct sipe_backend_codec *codec)
604 /* Not explicitly documented, but return value must be g_free()'d */
605 return purple_media_codec_get_encoding_name((PurpleMediaCodec *)codec);
608 guint
609 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
611 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
614 void
615 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec,
616 const gchar *name, const gchar *value)
618 purple_media_codec_add_optional_parameter((PurpleMediaCodec *)codec, name, value);
621 GList *
622 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
624 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
627 gboolean
628 sipe_backend_set_remote_codecs(struct sipe_backend_media *media,
629 struct sipe_backend_stream *stream,
630 GList *codecs)
632 return purple_media_set_remote_codecs(media->m,
633 stream->sessionid,
634 stream->participant,
635 codecs);
638 GList*
639 sipe_backend_get_local_codecs(struct sipe_backend_media *media,
640 struct sipe_backend_stream *stream)
642 GList *codecs = purple_media_get_codecs(media->m,
643 stream->sessionid);
644 GList *i = codecs;
645 gboolean is_conference = (g_strstr_len(stream->participant,
646 strlen(stream->participant),
647 "app:conf:audio-video:") != NULL);
650 * Do not announce Theora. Its optional parameters are too long,
651 * Communicator rejects such SDP message and does not support the codec
652 * anyway.
654 * For some yet unknown reason, A/V conferencing server does not accept
655 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
656 * we are able to decode incoming SIREN from server and with MSOC
657 * client, bidirectional call using the codec works. Until resolved,
658 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
659 * seems to work properly in this scenario.
661 while (i) {
662 PurpleMediaCodec *codec = i->data;
663 gchar *encoding_name = purple_media_codec_get_encoding_name(codec);
665 if (sipe_strequal(encoding_name,"THEORA") ||
666 (is_conference && sipe_strequal(encoding_name,"SIREN"))) {
667 GList *tmp;
668 g_object_unref(codec);
669 tmp = i->next;
670 codecs = g_list_delete_link(codecs, i);
671 i = tmp;
672 } else
673 i = i->next;
675 g_free(encoding_name);
678 return codecs;
681 struct sipe_backend_candidate *
682 sipe_backend_candidate_new(const gchar *foundation,
683 SipeComponentType component,
684 SipeCandidateType type, SipeNetworkProtocol proto,
685 const gchar *ip, guint port,
686 const gchar *username,
687 const gchar *password)
689 PurpleMediaCandidate *c = purple_media_candidate_new(
690 /* Libnice and Farsight rely on non-NULL foundation to
691 * distinguish between candidates of a component. When NULL
692 * foundation is passed (ie. ICE draft 6 does not use foudation),
693 * use username instead. If no foundation is provided, Farsight
694 * may signal an active candidate different from the one actually
695 * in use. See Farsight's agent_new_selected_pair() in
696 * fs-nice-stream-transmitter.h where first candidate in the
697 * remote list is always selected when no foundation. */
698 foundation ? foundation : username,
699 component,
700 sipe_candidate_type_to_purple(type),
701 sipe_network_protocol_to_purple(proto),
703 port);
704 g_object_set(c, "username", username, "password", password, NULL);
705 return (struct sipe_backend_candidate *)c;
708 void
709 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
711 if (candidate)
712 g_object_unref(candidate);
715 gchar *
716 sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate)
718 /* Not explicitly documented, but return value must be g_free()'d */
719 return purple_media_candidate_get_username((PurpleMediaCandidate*)candidate);
722 gchar *
723 sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate)
725 /* Not explicitly documented, but return value must be g_free()'d */
726 return purple_media_candidate_get_password((PurpleMediaCandidate*)candidate);
729 gchar *
730 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate)
732 /* Not explicitly documented, but return value must be g_free()'d */
733 return purple_media_candidate_get_foundation((PurpleMediaCandidate*)candidate);
736 gchar *
737 sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate)
739 /* Not explicitly documented, but return value must be g_free()'d */
740 return purple_media_candidate_get_ip((PurpleMediaCandidate*)candidate);
743 guint
744 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
746 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
749 gchar *
750 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate)
752 /* Not explicitly documented, but return value must be g_free()'d */
753 return purple_media_candidate_get_base_ip((PurpleMediaCandidate*)candidate);
756 guint
757 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
759 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
762 guint32
763 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
765 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
768 void
769 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
771 g_object_set(candidate, "priority", priority, NULL);
774 SipeComponentType
775 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
777 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
780 SipeCandidateType
781 sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate)
783 PurpleMediaCandidateType type =
784 purple_media_candidate_get_candidate_type((PurpleMediaCandidate*)candidate);
785 return purple_candidate_type_to_sipe(type);
788 SipeNetworkProtocol
789 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate)
791 PurpleMediaNetworkProtocol proto =
792 purple_media_candidate_get_protocol((PurpleMediaCandidate*)candidate);
793 return purple_network_protocol_to_sipe(proto);
796 static void
797 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
798 gpointer value,
799 gpointer user_data)
801 GList *entry = value;
802 GList **candidates = user_data;
804 g_object_unref(entry->data);
805 *candidates = g_list_delete_link(*candidates, entry);
808 static GList *
809 ensure_candidate_pairs(GList *candidates)
811 GHashTable *lone_cand_links;
812 GList *i;
814 lone_cand_links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
816 for (i = candidates; i; i = i->next) {
817 PurpleMediaCandidate *c = i->data;
818 gchar *foundation = purple_media_candidate_get_foundation(c);
820 if (g_hash_table_lookup(lone_cand_links, foundation)) {
821 g_hash_table_remove(lone_cand_links, foundation);
822 g_free(foundation);
823 } else {
824 g_hash_table_insert(lone_cand_links, foundation, i);
828 g_hash_table_foreach(lone_cand_links, remove_lone_candidate_cb, &candidates);
829 g_hash_table_destroy(lone_cand_links);
831 return candidates;
834 GList *
835 sipe_backend_get_local_candidates(struct sipe_backend_media *media,
836 struct sipe_backend_stream *stream)
838 GList *candidates = purple_media_get_local_candidates(media->m,
839 stream->sessionid,
840 stream->participant);
842 * Sometimes purple will not return complete list of candidates, even
843 * after "candidates-prepared" signal is emitted. This is a feature of
844 * libnice, namely affecting candidates discovered via UPnP. Nice does
845 * not wait until discovery is finished and can signal end of candidate
846 * gathering before all responses from UPnP enabled gateways are received.
848 * Remove any incomplete RTP+RTCP candidate pairs from the list.
850 candidates = ensure_candidate_pairs(candidates);
851 return candidates;
854 void
855 sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local)
857 if (media)
858 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT,
859 NULL, NULL, local);
862 void
863 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
865 if (media)
866 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
867 NULL, NULL, local);
870 void
871 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
873 if (media)
874 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
875 NULL, NULL, local);
878 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
880 switch (type) {
881 case SIPE_MEDIA_AUDIO: return PURPLE_MEDIA_AUDIO;
882 case SIPE_MEDIA_VIDEO: return PURPLE_MEDIA_VIDEO;
883 default: return PURPLE_MEDIA_NONE;
887 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
889 switch (type) {
890 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
891 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
892 default: return SIPE_MEDIA_AUDIO;
896 static PurpleMediaCandidateType
897 sipe_candidate_type_to_purple(SipeCandidateType type)
899 switch (type) {
900 case SIPE_CANDIDATE_TYPE_HOST: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
901 case SIPE_CANDIDATE_TYPE_RELAY: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
902 case SIPE_CANDIDATE_TYPE_SRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
903 case SIPE_CANDIDATE_TYPE_PRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX;
904 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
908 static SipeCandidateType
909 purple_candidate_type_to_sipe(PurpleMediaCandidateType type)
911 switch (type) {
912 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: return SIPE_CANDIDATE_TYPE_HOST;
913 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: return SIPE_CANDIDATE_TYPE_RELAY;
914 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: return SIPE_CANDIDATE_TYPE_SRFLX;
915 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: return SIPE_CANDIDATE_TYPE_PRFLX;
916 default: return SIPE_CANDIDATE_TYPE_HOST;
920 static PurpleMediaNetworkProtocol
921 sipe_network_protocol_to_purple(SipeNetworkProtocol proto)
923 switch (proto) {
924 #ifdef HAVE_ICE_TCP
925 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
926 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE;
927 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
928 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE;
929 #else
930 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
931 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
932 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP;
933 #endif
934 case SIPE_NETWORK_PROTOCOL_UDP:
935 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
936 default:
937 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
941 static SipeNetworkProtocol
942 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto)
944 switch (proto) {
945 #ifdef HAVE_ICE_TCP
946 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE:
947 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
948 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE:
949 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
950 #else
951 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP:
952 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
953 #endif
954 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
955 return SIPE_NETWORK_PROTOCOL_UDP;
956 default:
957 return SIPE_NETWORK_PROTOCOL_UDP;
962 Local Variables:
963 mode: c
964 c-file-style: "bsd"
965 indent-tabs-mode: t
966 tab-width: 8
967 End: