media: distinguish TCP active and passive candidates
[siplcs.git] / src / purple / purple-media.c
blob8c850562a30abacd9cbc72f01aa03ee80e0e4701
1 /**
2 * @file purple-media.c
4 * pidgin-sipe
6 * Copyright (C) 2010 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 #include "glib.h"
24 #include "glib/gstdio.h"
25 #include <fcntl.h>
26 #include <string.h>
27 #include <unistd.h>
29 #include "sipe-common.h"
31 #include "mediamanager.h"
32 #include "media-gst.h"
33 #include "request.h"
34 #include "agent.h"
36 #include "sipe-backend.h"
37 #include "sipe-core.h"
39 #include "purple-private.h"
41 struct sipe_backend_media {
42 PurpleMedia *m;
43 GSList *streams;
44 /**
45 * Number of media streams that were not yet locally accepted or rejected.
47 guint unconfirmed_streams;
50 struct sipe_backend_stream {
51 gchar *sessionid;
52 gchar *participant;
53 gboolean candidates_prepared;
54 gboolean local_on_hold;
55 gboolean remote_on_hold;
56 gboolean accepted;
59 static void
60 backend_stream_free(struct sipe_backend_stream *stream)
62 if (stream) {
63 g_free(stream->sessionid);
64 g_free(stream->participant);
65 g_free(stream);
69 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type);
70 static PurpleMediaCandidateType sipe_candidate_type_to_purple(SipeCandidateType type);
71 static SipeCandidateType purple_candidate_type_to_sipe(PurpleMediaCandidateType type);
72 static PurpleMediaNetworkProtocol sipe_network_protocol_to_purple(SipeNetworkProtocol proto);
73 static SipeNetworkProtocol purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto);
75 static void
76 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
77 gchar *sessionid,
78 SIPE_UNUSED_PARAMETER gchar *participant,
79 struct sipe_media_call *call)
81 struct sipe_backend_stream *stream;
82 stream = sipe_backend_media_get_stream_by_id(call->backend_private, sessionid);
84 stream->candidates_prepared = TRUE;
86 if (call->candidates_prepared_cb &&
87 sipe_backend_candidates_prepared(call->backend_private)) {
88 call->candidates_prepared_cb(call, stream);
92 static void
93 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
94 PurpleMediaState state,
95 gchar *sessionid,
96 gchar *participant,
97 struct sipe_media_call *call)
99 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state, sessionid, participant);
100 if (state == PURPLE_MEDIA_STATE_END &&
101 !sessionid && !participant && call->media_end_cb)
102 call->media_end_cb(call);
105 /* Used externally in purple-plugin.c. This declaration stops the compiler
106 * complaining about missing prototype. */
107 void capture_pipeline(gchar *label);
109 void
110 capture_pipeline(gchar *label) {
111 PurpleMediaManager *manager = purple_media_manager_get();
112 GstElement *pipeline = purple_media_manager_get_pipeline(manager);
113 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, label);
116 static void
117 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *message,
118 struct sipe_media_call *call)
120 capture_pipeline("ERROR");
122 if (call->error_cb)
123 call->error_cb(call, message);
126 static void
127 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
128 PurpleMediaInfoType type,
129 gchar *sessionid,
130 gchar *participant,
131 gboolean local,
132 struct sipe_media_call *call)
134 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
135 if (call->call_accept_cb && !sessionid && !participant)
136 call->call_accept_cb(call, local);
137 else if (sessionid && participant) {
138 struct sipe_backend_stream *stream;
139 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
140 sessionid);
141 if (stream) {
142 if (!stream->accepted && local)
143 --call->backend_private->unconfirmed_streams;
144 stream->accepted = TRUE;
147 } else if (type == PURPLE_MEDIA_INFO_HOLD || type == PURPLE_MEDIA_INFO_UNHOLD) {
149 gboolean state = (type == PURPLE_MEDIA_INFO_HOLD);
151 if (sessionid) {
152 // Hold specific stream
153 struct sipe_backend_stream *stream;
154 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
155 sessionid);
157 if (local)
158 stream->local_on_hold = state;
159 else
160 stream->remote_on_hold = state;
161 } else {
162 // Hold all streams
163 GSList *i = sipe_backend_media_get_streams(call->backend_private);
164 for (; i; i = i->next) {
165 struct sipe_backend_stream *stream = i->data;
167 if (local)
168 stream->local_on_hold = state;
169 else
170 stream->remote_on_hold = state;
174 if (call->call_hold_cb)
175 call->call_hold_cb(call, local, state);
176 } else if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) {
177 if (!sessionid && !participant) {
178 if (type == PURPLE_MEDIA_INFO_HANGUP && call->call_hangup_cb)
179 call->call_hangup_cb(call, local);
180 else if (type == PURPLE_MEDIA_INFO_REJECT && call->call_reject_cb && !local)
181 call->call_reject_cb(call, local);
182 } else if (sessionid && participant) {
183 struct sipe_backend_stream *stream;
184 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
185 sessionid);
187 if (stream) {
188 call->backend_private->streams = g_slist_remove(call->backend_private->streams, stream);
189 backend_stream_free(stream);
190 if (local && --call->backend_private->unconfirmed_streams == 0 &&
191 call->call_reject_cb)
192 call->call_reject_cb(call, local);
198 struct sipe_backend_media *
199 sipe_backend_media_new(struct sipe_core_public *sipe_public,
200 struct sipe_media_call *call,
201 const gchar *participant,
202 gboolean initiator)
204 struct sipe_backend_media *media = g_new0(struct sipe_backend_media, 1);
205 struct sipe_backend_private *purple_private = sipe_public->backend_private;
206 PurpleMediaManager *manager = purple_media_manager_get();
207 GstElement *pipeline;
209 media->m = purple_media_manager_create_media(manager,
210 purple_private->account,
211 "fsrtpconference",
212 participant, initiator);
214 g_signal_connect(G_OBJECT(media->m), "candidates-prepared",
215 G_CALLBACK(on_candidates_prepared_cb), call);
216 g_signal_connect(G_OBJECT(media->m), "stream-info",
217 G_CALLBACK(on_stream_info_cb), call);
218 g_signal_connect(G_OBJECT(media->m), "error",
219 G_CALLBACK(on_error_cb), call);
220 g_signal_connect(G_OBJECT(media->m), "state-changed",
221 G_CALLBACK(on_state_changed_cb), call);
223 /* On error, the pipeline is no longer in PLAYING state and libpurple
224 * will not switch it back to PLAYING, preventing any more calls until
225 * application restart. We switch the state ourselves here to negate
226 * effect of any error in previous call (if any). */
227 pipeline = purple_media_manager_get_pipeline(manager);
228 gst_element_set_state(pipeline, GST_STATE_PLAYING);
230 return media;
233 void
234 sipe_backend_media_free(struct sipe_backend_media *media)
236 if (media) {
237 GSList *stream = media->streams;
239 for (; stream; stream = g_slist_delete_link(stream, stream))
240 backend_stream_free(stream->data);
242 g_free(media);
246 void
247 sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname)
249 if (media) {
250 guint num_params = 3;
251 GParameter *params = g_new0(GParameter, num_params);
252 params[0].name = "sdes-cname";
253 g_value_init(&params[0].value, G_TYPE_STRING);
254 g_value_set_string(&params[0].value, cname);
255 params[1].name = "sdes-name";
256 g_value_init(&params[1].value, G_TYPE_STRING);
257 params[2].name = "sdes-tool";
258 g_value_init(&params[2].value, G_TYPE_STRING);
260 purple_media_set_params(media->m, num_params, params);
262 g_value_unset(&params[0].value);
263 g_free(params);
267 #define FS_CODECS_CONF \
268 "# Automatically created by SIPE plugin\n" \
269 "[video/H263]\n" \
270 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
271 "\n" \
272 "[audio/PCMA]\n" \
273 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
274 "\n" \
275 "[audio/PCMU]\n" \
276 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
278 static void
279 ensure_codecs_conf()
281 gchar *filename;
282 filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
284 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
285 int fd = g_open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
286 gchar *fs_codecs_conf = FS_CODECS_CONF;
287 if (!fd || write(fd, fs_codecs_conf, strlen(fs_codecs_conf)) == -1)
288 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
289 close(fd);
292 g_free(filename);
295 static void
296 append_relay(GValueArray *relay_info, const gchar *ip, guint port, gchar *type,
297 gchar *username, gchar *password)
299 GValue value;
300 GstStructure *gst_relay_info;
302 gst_relay_info = gst_structure_new("relay-info",
303 "ip", G_TYPE_STRING, ip,
304 "port", G_TYPE_UINT, port,
305 "relay-type", G_TYPE_STRING, type,
306 "username", G_TYPE_STRING, username,
307 "password", G_TYPE_STRING, password,
308 NULL);
310 if (gst_relay_info) {
311 memset(&value, 0, sizeof(GValue));
312 g_value_init(&value, GST_TYPE_STRUCTURE);
313 gst_value_set_structure(&value, gst_relay_info);
315 g_value_array_append(relay_info, &value);
316 gst_structure_free(gst_relay_info);
320 struct sipe_backend_media_relays *
321 sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password)
323 GValueArray *relay_info = g_value_array_new(0);
325 for (; media_relays; media_relays = media_relays->next) {\
326 struct sipe_media_relay *relay = media_relays->data;
328 /* Skip relays where IP could not be resolved. */
329 if (!relay->hostname)
330 continue;
332 if (relay->udp_port != 0)
333 append_relay(relay_info, relay->hostname, relay->udp_port,
334 "udp", username, password);
336 if (relay->tcp_port != 0)
337 append_relay(relay_info, relay->hostname, relay->tcp_port,
338 "tcp", username, password);
341 return (struct sipe_backend_media_relays *)relay_info;
344 void
345 sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays)
347 g_value_array_free((GValueArray *)media_relays);
350 struct sipe_backend_stream *
351 sipe_backend_media_add_stream(struct sipe_backend_media *media,
352 const gchar *id,
353 const gchar *participant,
354 SipeMediaType type,
355 SipeIceVersion ice_version,
356 gboolean initiator,
357 struct sipe_backend_media_relays *media_relays)
359 struct sipe_backend_stream *stream = NULL;
360 PurpleMediaSessionType prpl_type = sipe_media_to_purple(type);
361 GParameter *params = NULL;
362 guint params_cnt = 0;
363 gchar *transmitter;
365 if (ice_version != SIPE_ICE_NO_ICE) {
366 transmitter = "nice";
367 params_cnt = 2;
369 params = g_new0(GParameter, params_cnt);
371 params[0].name = "compatibility-mode";
372 g_value_init(&params[0].value, G_TYPE_UINT);
373 g_value_set_uint(&params[0].value,
374 ice_version == SIPE_ICE_DRAFT_6 ?
375 NICE_COMPATIBILITY_OC2007 :
376 NICE_COMPATIBILITY_OC2007R2);
378 if (media_relays) {
379 params[1].name = "relay-info";
380 g_value_init(&params[1].value, G_TYPE_VALUE_ARRAY);
381 g_value_set_boxed(&params[1].value, media_relays);
382 } else
383 --params_cnt;
384 } else {
385 // TODO: session naming here, Communicator needs audio/video
386 transmitter = "rawudp";
387 //sessionid = "sipe-voice-rawudp";
390 ensure_codecs_conf();
392 if (purple_media_add_stream(media->m, id, participant, prpl_type,
393 initiator, transmitter, params_cnt,
394 params)) {
395 stream = g_new0(struct sipe_backend_stream, 1);
396 stream->sessionid = g_strdup(id);
397 stream->participant = g_strdup(participant);
398 stream->candidates_prepared = FALSE;
400 media->streams = g_slist_append(media->streams, stream);
401 if (!initiator)
402 ++media->unconfirmed_streams;
405 if (media_relays)
406 g_value_unset(&params[1].value);
408 g_free(params);
410 return stream;
413 void
414 sipe_backend_media_remove_stream(struct sipe_backend_media *media,
415 struct sipe_backend_stream *stream)
417 g_return_if_fail(media && stream);
419 purple_media_end(media->m, stream->sessionid, NULL);
420 media->streams = g_slist_remove(media->streams, stream);
421 backend_stream_free(stream);
424 GSList *sipe_backend_media_get_streams(struct sipe_backend_media *media)
426 return media->streams;
429 struct sipe_backend_stream *
430 sipe_backend_media_get_stream_by_id(struct sipe_backend_media *media,
431 const gchar *id)
433 GSList *i;
434 for (i = media->streams; i; i = i->next) {
435 struct sipe_backend_stream *stream = i->data;
436 if (sipe_strequal(stream->sessionid, id))
437 return stream;
439 return NULL;
442 void
443 sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
444 struct sipe_backend_stream *stream,
445 GList *candidates)
447 purple_media_add_remote_candidates(media->m, stream->sessionid,
448 stream->participant, candidates);
451 gboolean sipe_backend_media_is_initiator(struct sipe_backend_media *media,
452 struct sipe_backend_stream *stream)
454 return purple_media_is_initiator(media->m,
455 stream ? stream->sessionid : NULL,
456 stream ? stream->participant : NULL);
459 gboolean sipe_backend_media_accepted(struct sipe_backend_media *media)
461 return purple_media_accepted(media->m, NULL, NULL);
464 gboolean
465 sipe_backend_candidates_prepared(struct sipe_backend_media *media)
467 GSList *streams = media->streams;
468 for (; streams; streams = streams->next) {
469 struct sipe_backend_stream *s = streams->data;
470 if (!s->candidates_prepared)
471 return FALSE;
473 return TRUE;
476 GList *
477 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media *media,
478 struct sipe_backend_stream *stream)
480 return purple_media_get_active_local_candidates(media->m,
481 stream->sessionid,
482 stream->participant);
485 GList *
486 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media *media,
487 struct sipe_backend_stream *stream)
489 return purple_media_get_active_remote_candidates(media->m,
490 stream->sessionid,
491 stream->participant);
494 gchar *
495 sipe_backend_stream_get_id(struct sipe_backend_stream *stream)
497 return stream->sessionid;
500 void sipe_backend_stream_hold(struct sipe_backend_media *media,
501 struct sipe_backend_stream *stream,
502 gboolean local)
504 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HOLD,
505 stream->sessionid, stream->participant,
506 local);
509 void sipe_backend_stream_unhold(struct sipe_backend_media *media,
510 struct sipe_backend_stream *stream,
511 gboolean local)
513 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_UNHOLD,
514 stream->sessionid, stream->participant,
515 local);
518 gboolean sipe_backend_stream_is_held(struct sipe_backend_stream *stream)
520 g_return_val_if_fail(stream, FALSE);
522 return stream->local_on_hold || stream->remote_on_hold;
525 struct sipe_backend_codec *
526 sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate)
528 return (struct sipe_backend_codec *)purple_media_codec_new(id, name,
529 sipe_media_to_purple(type),
530 clock_rate);
533 void
534 sipe_backend_codec_free(struct sipe_backend_codec *codec)
536 if (codec)
537 g_object_unref(codec);
541 sipe_backend_codec_get_id(struct sipe_backend_codec *codec)
543 return purple_media_codec_get_id((PurpleMediaCodec *)codec);
546 gchar *
547 sipe_backend_codec_get_name(struct sipe_backend_codec *codec)
549 /* Not explicitly documented, but return value must be g_free()'d */
550 return purple_media_codec_get_encoding_name((PurpleMediaCodec *)codec);
553 guint
554 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
556 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
559 void
560 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec,
561 const gchar *name, const gchar *value)
563 purple_media_codec_add_optional_parameter((PurpleMediaCodec *)codec, name, value);
566 GList *
567 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
569 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
572 gboolean
573 sipe_backend_set_remote_codecs(struct sipe_backend_media *media,
574 struct sipe_backend_stream *stream,
575 GList *codecs)
577 return purple_media_set_remote_codecs(media->m,
578 stream->sessionid,
579 stream->participant,
580 codecs);
583 GList*
584 sipe_backend_get_local_codecs(struct sipe_backend_media *media,
585 struct sipe_backend_stream *stream)
587 GList *codecs = purple_media_get_codecs(media->m,
588 stream->sessionid);
589 GList *i = codecs;
590 gboolean is_conference = (g_strstr_len(stream->participant,
591 strlen(stream->participant),
592 "app:conf:audio-video:") != NULL);
595 * Do not announce Theora. Its optional parameters are too long,
596 * Communicator rejects such SDP message and does not support the codec
597 * anyway.
599 * For some yet unknown reason, A/V conferencing server does not accept
600 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
601 * we are able to decode incoming SIREN from server and with MSOC
602 * client, bidirectional call using the codec works. Until resolved,
603 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
604 * seems to work properly in this scenario.
606 while (i) {
607 PurpleMediaCodec *codec = i->data;
608 gchar *encoding_name = purple_media_codec_get_encoding_name(codec);
610 if (sipe_strequal(encoding_name,"THEORA") ||
611 (is_conference && sipe_strequal(encoding_name,"SIREN"))) {
612 GList *tmp;
613 g_object_unref(codec);
614 tmp = i->next;
615 codecs = g_list_delete_link(codecs, i);
616 i = tmp;
617 } else
618 i = i->next;
620 g_free(encoding_name);
623 return codecs;
626 struct sipe_backend_candidate *
627 sipe_backend_candidate_new(const gchar *foundation,
628 SipeComponentType component,
629 SipeCandidateType type, SipeNetworkProtocol proto,
630 const gchar *ip, guint port,
631 const gchar *username,
632 const gchar *password)
634 PurpleMediaCandidate *c = purple_media_candidate_new(
635 /* Libnice and Farsight rely on non-NULL foundation to
636 * distinguish between candidates of a component. When NULL
637 * foundation is passed (ie. ICE draft 6 does not use foudation),
638 * use username instead. If no foundation is provided, Farsight
639 * may signal an active candidate different from the one actually
640 * in use. See Farsight's agent_new_selected_pair() in
641 * fs-nice-stream-transmitter.h where first candidate in the
642 * remote list is always selected when no foundation. */
643 foundation ? foundation : username,
644 component,
645 sipe_candidate_type_to_purple(type),
646 sipe_network_protocol_to_purple(proto),
648 port);
649 g_object_set(c, "username", username, "password", password, NULL);
650 return (struct sipe_backend_candidate *)c;
653 void
654 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
656 if (candidate)
657 g_object_unref(candidate);
660 gchar *
661 sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate)
663 /* Not explicitly documented, but return value must be g_free()'d */
664 return purple_media_candidate_get_username((PurpleMediaCandidate*)candidate);
667 gchar *
668 sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate)
670 /* Not explicitly documented, but return value must be g_free()'d */
671 return purple_media_candidate_get_password((PurpleMediaCandidate*)candidate);
674 gchar *
675 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate)
677 /* Not explicitly documented, but return value must be g_free()'d */
678 return purple_media_candidate_get_foundation((PurpleMediaCandidate*)candidate);
681 gchar *
682 sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate)
684 /* Not explicitly documented, but return value must be g_free()'d */
685 return purple_media_candidate_get_ip((PurpleMediaCandidate*)candidate);
688 guint
689 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
691 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
694 gchar *
695 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate)
697 /* Not explicitly documented, but return value must be g_free()'d */
698 return purple_media_candidate_get_base_ip((PurpleMediaCandidate*)candidate);
701 guint
702 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
704 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
707 guint32
708 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
710 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
713 void
714 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
716 g_object_set(candidate, "priority", priority, NULL);
719 SipeComponentType
720 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
722 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
725 SipeCandidateType
726 sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate)
728 PurpleMediaCandidateType type =
729 purple_media_candidate_get_candidate_type((PurpleMediaCandidate*)candidate);
730 return purple_candidate_type_to_sipe(type);
733 SipeNetworkProtocol
734 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate)
736 PurpleMediaNetworkProtocol proto =
737 purple_media_candidate_get_protocol((PurpleMediaCandidate*)candidate);
738 return purple_network_protocol_to_sipe(proto);
741 static void
742 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
743 gpointer value,
744 gpointer user_data)
746 GList *entry = value;
747 GList **candidates = user_data;
749 g_object_unref(entry->data);
750 *candidates = g_list_delete_link(*candidates, entry);
753 static GList *
754 ensure_candidate_pairs(GList *candidates)
756 GHashTable *lone_cand_links;
757 GList *i;
759 lone_cand_links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
761 for (i = candidates; i; i = i->next) {
762 PurpleMediaCandidate *c = i->data;
763 gchar *foundation = purple_media_candidate_get_foundation(c);
765 if (g_hash_table_lookup(lone_cand_links, foundation)) {
766 g_hash_table_remove(lone_cand_links, foundation);
767 g_free(foundation);
768 } else {
769 g_hash_table_insert(lone_cand_links, foundation, i);
773 g_hash_table_foreach(lone_cand_links, remove_lone_candidate_cb, &candidates);
774 g_hash_table_destroy(lone_cand_links);
776 return candidates;
779 GList *
780 sipe_backend_get_local_candidates(struct sipe_backend_media *media,
781 struct sipe_backend_stream *stream)
783 GList *candidates = purple_media_get_local_candidates(media->m,
784 stream->sessionid,
785 stream->participant);
787 * Sometimes purple will not return complete list of candidates, even
788 * after "candidates-prepared" signal is emitted. This is a feature of
789 * libnice, namely affecting candidates discovered via UPnP. Nice does
790 * not wait until discovery is finished and can signal end of candidate
791 * gathering before all responses from UPnP enabled gateways are received.
793 * Remove any incomplete RTP+RTCP candidate pairs from the list.
795 candidates = ensure_candidate_pairs(candidates);
796 return candidates;
799 void
800 sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local)
802 if (media)
803 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT,
804 NULL, NULL, local);
807 void
808 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
810 if (media)
811 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
812 NULL, NULL, local);
815 void
816 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
818 if (media)
819 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
820 NULL, NULL, local);
823 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
825 switch (type) {
826 case SIPE_MEDIA_AUDIO: return PURPLE_MEDIA_AUDIO;
827 case SIPE_MEDIA_VIDEO: return PURPLE_MEDIA_VIDEO;
828 default: return PURPLE_MEDIA_NONE;
832 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
834 switch (type) {
835 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
836 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
837 default: return SIPE_MEDIA_AUDIO;
841 static PurpleMediaCandidateType
842 sipe_candidate_type_to_purple(SipeCandidateType type)
844 switch (type) {
845 case SIPE_CANDIDATE_TYPE_HOST: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
846 case SIPE_CANDIDATE_TYPE_RELAY: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
847 case SIPE_CANDIDATE_TYPE_SRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
848 case SIPE_CANDIDATE_TYPE_PRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX;
849 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
853 static SipeCandidateType
854 purple_candidate_type_to_sipe(PurpleMediaCandidateType type)
856 switch (type) {
857 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: return SIPE_CANDIDATE_TYPE_HOST;
858 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: return SIPE_CANDIDATE_TYPE_RELAY;
859 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: return SIPE_CANDIDATE_TYPE_SRFLX;
860 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: return SIPE_CANDIDATE_TYPE_PRFLX;
861 default: return SIPE_CANDIDATE_TYPE_HOST;
865 static PurpleMediaNetworkProtocol
866 sipe_network_protocol_to_purple(SipeNetworkProtocol proto)
868 switch (proto) {
869 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
870 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE;
871 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
872 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE;
873 case SIPE_NETWORK_PROTOCOL_UDP:
874 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
875 default:
876 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
880 static SipeNetworkProtocol
881 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto)
883 switch (proto) {
884 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE:
885 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
886 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE:
887 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
888 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
889 return SIPE_NETWORK_PROTOCOL_UDP;
890 default:
891 return SIPE_NETWORK_PROTOCOL_UDP;
896 Local Variables:
897 mode: c
898 c-file-style: "bsd"
899 indent-tabs-mode: t
900 tab-width: 8
901 End: