purple: fix crash on call initiation failure
[siplcs.git] / src / purple / purple-media.c
blobc61208b977cce76135c37e9a620bf7b6d7c3a3f6
1 /**
2 * @file purple-media.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2014 SIPE Project <http://sipe.sourceforge.net/>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
27 #include "glib.h"
28 #include "glib/gstdio.h"
29 #include <fcntl.h>
30 #include <string.h>
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
35 #include "sipe-common.h"
37 #include "mediamanager.h"
38 #include "agent.h"
40 #ifdef _WIN32
41 /* wrappers for write() & friends for socket handling */
42 #include "win32/win32dep.h"
43 #endif
45 #include "sipe-backend.h"
46 #include "sipe-core.h"
48 #include "purple-private.h"
51 * GStreamer interfaces fail to compile on ARM architecture with -Wcast-align
53 * Diagnostic #pragma was added in GCC 4.2.0
55 #if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 2)
56 #if defined(__ARMEL__) || defined(__ARMEB__) || defined(__mips__) || defined(__sparc__) || (defined(__powerpc__) && defined(__NO_FPRS__))
57 #pragma GCC diagnostic ignored "-Wcast-align"
58 #endif
59 #endif
61 #include "media-gst.h"
63 struct sipe_backend_media {
64 struct sipe_media_call *call;
65 PurpleMedia *m;
66 GSList *streams;
67 /**
68 * Number of media streams that were not yet locally accepted or rejected.
70 guint unconfirmed_streams;
73 struct sipe_backend_stream {
74 gchar *sessionid;
75 gchar *participant;
76 gboolean local_on_hold;
77 gboolean remote_on_hold;
78 gboolean accepted;
79 gboolean initialized_cb_was_fired;
82 static void
83 backend_stream_free(struct sipe_backend_stream *stream)
85 if (stream) {
86 g_free(stream->sessionid);
87 g_free(stream->participant);
88 g_free(stream);
92 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type);
93 static PurpleMediaCandidateType sipe_candidate_type_to_purple(SipeCandidateType type);
94 static SipeCandidateType purple_candidate_type_to_sipe(PurpleMediaCandidateType type);
95 static PurpleMediaNetworkProtocol sipe_network_protocol_to_purple(SipeNetworkProtocol proto);
96 static SipeNetworkProtocol purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto);
98 static void
99 maybe_signal_stream_initialized(struct sipe_media_call *call, gchar *sessionid)
101 if (call->stream_initialized_cb) {
102 struct sipe_backend_stream *stream;
103 stream = sipe_backend_media_get_stream_by_id(call->backend_private, sessionid);
105 if (sipe_backend_stream_initialized(call->backend_private, stream) &&
106 !stream->initialized_cb_was_fired) {
107 call->stream_initialized_cb(call, stream);
108 stream->initialized_cb_was_fired = TRUE;
113 static void
114 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
115 gchar *sessionid,
116 SIPE_UNUSED_PARAMETER gchar *participant,
117 struct sipe_media_call *call)
119 maybe_signal_stream_initialized(call, sessionid);
122 static void
123 on_codecs_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
124 gchar *sessionid,
125 struct sipe_media_call *call)
127 maybe_signal_stream_initialized(call, sessionid);
130 static void
131 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
132 PurpleMediaState state,
133 gchar *sessionid,
134 gchar *participant,
135 struct sipe_media_call *call)
137 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state, sessionid, participant);
138 if (state == PURPLE_MEDIA_STATE_END &&
139 !sessionid && !participant && call->media_end_cb)
140 call->media_end_cb(call);
143 void
144 capture_pipeline(const gchar *label) {
145 PurpleMediaManager *manager = purple_media_manager_get();
146 GstElement *pipeline = purple_media_manager_get_pipeline(manager);
147 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, label);
150 static void
151 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *message,
152 struct sipe_media_call *call)
154 capture_pipeline("ERROR");
156 if (call->error_cb)
157 call->error_cb(call, message);
160 static void
161 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
162 PurpleMediaInfoType type,
163 gchar *sessionid,
164 gchar *participant,
165 gboolean local,
166 struct sipe_media_call *call)
168 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
169 if (call->call_accept_cb && !sessionid && !participant)
170 call->call_accept_cb(call, local);
171 else if (sessionid && participant) {
172 struct sipe_backend_stream *stream;
173 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
174 sessionid);
175 if (stream) {
176 if (!stream->accepted && local)
177 --call->backend_private->unconfirmed_streams;
178 stream->accepted = TRUE;
181 } else if (type == PURPLE_MEDIA_INFO_HOLD || type == PURPLE_MEDIA_INFO_UNHOLD) {
183 gboolean state = (type == PURPLE_MEDIA_INFO_HOLD);
185 if (sessionid) {
186 // Hold specific stream
187 struct sipe_backend_stream *stream;
188 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
189 sessionid);
191 if (local)
192 stream->local_on_hold = state;
193 else
194 stream->remote_on_hold = state;
195 } else {
196 // Hold all streams
197 GSList *i = sipe_backend_media_get_streams(call->backend_private);
198 for (; i; i = i->next) {
199 struct sipe_backend_stream *stream = i->data;
201 if (local)
202 stream->local_on_hold = state;
203 else
204 stream->remote_on_hold = state;
208 if (call->call_hold_cb)
209 call->call_hold_cb(call, local, state);
210 } else if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) {
211 if (!sessionid && !participant) {
212 if (type == PURPLE_MEDIA_INFO_HANGUP && call->call_hangup_cb)
213 call->call_hangup_cb(call, local);
214 else if (type == PURPLE_MEDIA_INFO_REJECT && call->call_reject_cb && !local)
215 call->call_reject_cb(call, local);
216 } else if (sessionid && participant) {
217 struct sipe_backend_stream *stream;
218 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
219 sessionid);
221 if (stream) {
222 call->backend_private->streams = g_slist_remove(call->backend_private->streams, stream);
223 backend_stream_free(stream);
224 if (local && --call->backend_private->unconfirmed_streams == 0 &&
225 call->call_reject_cb)
226 call->call_reject_cb(call, local);
232 struct sipe_backend_media *
233 sipe_backend_media_new(struct sipe_core_public *sipe_public,
234 struct sipe_media_call *call,
235 const gchar *participant,
236 gboolean initiator)
238 struct sipe_backend_media *media = g_new0(struct sipe_backend_media, 1);
239 struct sipe_backend_private *purple_private = sipe_public->backend_private;
240 PurpleMediaManager *manager = purple_media_manager_get();
241 GstElement *pipeline;
243 media->call = call;
244 media->m = purple_media_manager_create_media(manager,
245 purple_private->account,
246 "fsrtpconference",
247 participant, initiator);
249 g_signal_connect(G_OBJECT(media->m), "candidates-prepared",
250 G_CALLBACK(on_candidates_prepared_cb), call);
251 g_signal_connect(G_OBJECT(media->m), "codecs-changed",
252 G_CALLBACK(on_codecs_changed_cb), call);
253 g_signal_connect(G_OBJECT(media->m), "stream-info",
254 G_CALLBACK(on_stream_info_cb), call);
255 g_signal_connect(G_OBJECT(media->m), "error",
256 G_CALLBACK(on_error_cb), call);
257 g_signal_connect(G_OBJECT(media->m), "state-changed",
258 G_CALLBACK(on_state_changed_cb), call);
260 /* On error, the pipeline is no longer in PLAYING state and libpurple
261 * will not switch it back to PLAYING, preventing any more calls until
262 * application restart. We switch the state ourselves here to negate
263 * effect of any error in previous call (if any). */
264 pipeline = purple_media_manager_get_pipeline(manager);
265 gst_element_set_state(pipeline, GST_STATE_PLAYING);
267 return media;
270 void
271 sipe_backend_media_free(struct sipe_backend_media *media)
273 if (media) {
274 GSList *stream = media->streams;
275 PurpleMediaManager *manager = purple_media_manager_get();
277 g_signal_handlers_disconnect_by_func(G_OBJECT(media->m),
278 G_CALLBACK(on_candidates_prepared_cb),
279 media->call);
280 g_signal_handlers_disconnect_by_func(G_OBJECT(media->m),
281 G_CALLBACK(on_codecs_changed_cb),
282 media->call);
283 g_signal_handlers_disconnect_by_func(G_OBJECT(media->m),
284 G_CALLBACK(on_stream_info_cb),
285 media->call);
286 g_signal_handlers_disconnect_by_func(G_OBJECT(media->m),
287 G_CALLBACK(on_error_cb),
288 media->call);
289 g_signal_handlers_disconnect_by_func(G_OBJECT(media->m),
290 G_CALLBACK(on_state_changed_cb),
291 media->call);
292 purple_media_manager_remove_media(manager, media->m);
294 for (; stream; stream = g_slist_delete_link(stream, stream))
295 backend_stream_free(stream->data);
297 g_free(media);
301 void
302 sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname)
304 if (media) {
305 guint num_params = 3;
306 GParameter *params = g_new0(GParameter, num_params);
307 params[0].name = "sdes-cname";
308 g_value_init(&params[0].value, G_TYPE_STRING);
309 g_value_set_string(&params[0].value, cname);
310 params[1].name = "sdes-name";
311 g_value_init(&params[1].value, G_TYPE_STRING);
312 params[2].name = "sdes-tool";
313 g_value_init(&params[2].value, G_TYPE_STRING);
315 purple_media_set_params(media->m, num_params, params);
317 g_value_unset(&params[0].value);
318 g_free(params);
322 #define FS_CODECS_CONF \
323 "# Automatically created by SIPE plugin\n" \
324 "[video/H263]\n" \
325 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
326 "\n" \
327 "[audio/PCMA]\n" \
328 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
329 "\n" \
330 "[audio/PCMU]\n" \
331 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
333 static void
334 ensure_codecs_conf()
336 gchar *filename;
337 filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
339 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
340 int fd = g_open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
341 gchar *fs_codecs_conf = FS_CODECS_CONF;
342 if ((fd < 0) || write(fd, fs_codecs_conf, strlen(fs_codecs_conf)) == -1)
343 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
344 if (fd >= 0)
345 close(fd);
348 g_free(filename);
351 static void
352 append_relay(GValueArray *relay_info, const gchar *ip, guint port, gchar *type,
353 gchar *username, gchar *password)
355 GValue value;
356 GstStructure *gst_relay_info;
358 gst_relay_info = gst_structure_new("relay-info",
359 "ip", G_TYPE_STRING, ip,
360 "port", G_TYPE_UINT, port,
361 "relay-type", G_TYPE_STRING, type,
362 "username", G_TYPE_STRING, username,
363 "password", G_TYPE_STRING, password,
364 NULL);
366 if (gst_relay_info) {
367 memset(&value, 0, sizeof(GValue));
368 g_value_init(&value, GST_TYPE_STRUCTURE);
369 gst_value_set_structure(&value, gst_relay_info);
371 g_value_array_append(relay_info, &value);
372 gst_structure_free(gst_relay_info);
376 struct sipe_backend_media_relays *
377 sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password)
379 GValueArray *relay_info = g_value_array_new(0);
381 for (; media_relays; media_relays = media_relays->next) {\
382 struct sipe_media_relay *relay = media_relays->data;
384 /* Skip relays where IP could not be resolved. */
385 if (!relay->hostname)
386 continue;
388 if (relay->udp_port != 0)
389 append_relay(relay_info, relay->hostname, relay->udp_port,
390 "udp", username, password);
392 #ifdef HAVE_ICE_TCP
393 if (relay->tcp_port != 0)
394 append_relay(relay_info, relay->hostname, relay->tcp_port,
395 "tcp", username, password);
396 #endif
399 return (struct sipe_backend_media_relays *)relay_info;
402 void
403 sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays)
405 g_value_array_free((GValueArray *)media_relays);
408 static guint
409 stream_demultiplex_cb(const gchar *buf, SIPE_UNUSED_PARAMETER gpointer *user_data)
411 guint8 payload_type = buf[1] & 0x7F;
412 if (payload_type >= 200 && payload_type <=204) {
413 // Looks like RTCP
414 return PURPLE_MEDIA_COMPONENT_RTCP;
415 } else {
416 // Looks like RTP
417 return PURPLE_MEDIA_COMPONENT_RTP;
421 struct sipe_backend_stream *
422 sipe_backend_media_add_stream(struct sipe_backend_media *media,
423 const gchar *id,
424 const gchar *participant,
425 SipeMediaType type,
426 SipeIceVersion ice_version,
427 gboolean initiator,
428 struct sipe_backend_media_relays *media_relays)
430 struct sipe_backend_stream *stream = NULL;
431 PurpleMediaSessionType prpl_type = sipe_media_to_purple(type);
432 GParameter *params = NULL;
433 guint params_cnt = 0;
434 gchar *transmitter;
436 if (ice_version != SIPE_ICE_NO_ICE) {
437 transmitter = "nice";
438 params_cnt = 4;
440 params = g_new0(GParameter, params_cnt);
442 params[0].name = "compatibility-mode";
443 g_value_init(&params[0].value, G_TYPE_UINT);
444 g_value_set_uint(&params[0].value,
445 ice_version == SIPE_ICE_DRAFT_6 ?
446 NICE_COMPATIBILITY_OC2007 :
447 NICE_COMPATIBILITY_OC2007R2);
449 params[1].name = "transport-protocols";
450 g_value_init(&params[1].value, G_TYPE_UINT);
451 #ifdef HAVE_ICE_TCP
452 g_value_set_uint(&params[1].value,
453 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP |
454 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE |
455 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE);
456 #else
457 g_value_set_uint(&params[1].value,
458 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP);
459 #endif
461 params[2].name = "demultiplex-func";
462 g_value_init(&params[2].value, G_TYPE_POINTER);
463 g_value_set_pointer(&params[2].value, stream_demultiplex_cb);
465 if (media_relays) {
466 params[3].name = "relay-info";
467 g_value_init(&params[3].value, G_TYPE_VALUE_ARRAY);
468 g_value_set_boxed(&params[3].value, media_relays);
469 } else
470 --params_cnt;
471 } else {
472 // TODO: session naming here, Communicator needs audio/video
473 transmitter = "rawudp";
474 //sessionid = "sipe-voice-rawudp";
476 /* To avoid Coverity FORWARD_NULL warning. params_cnt is
477 * still 0 so this is a no-op. libpurple API documentation
478 * doesn't specify if params can be NULL or not. */
479 params = g_new0(GParameter, 1);
482 ensure_codecs_conf();
484 if (purple_media_add_stream(media->m, id, participant, prpl_type,
485 initiator, transmitter, params_cnt,
486 params)) {
487 stream = g_new0(struct sipe_backend_stream, 1);
488 stream->sessionid = g_strdup(id);
489 stream->participant = g_strdup(participant);
490 stream->initialized_cb_was_fired = FALSE;
492 media->streams = g_slist_append(media->streams, stream);
493 if (!initiator)
494 ++media->unconfirmed_streams;
497 if ((params_cnt > 2) && media_relays)
498 g_value_unset(&params[3].value);
500 g_free(params);
502 return stream;
505 void
506 sipe_backend_media_remove_stream(struct sipe_backend_media *media,
507 struct sipe_backend_stream *stream)
509 g_return_if_fail(media && stream);
511 purple_media_end(media->m, stream->sessionid, NULL);
512 media->streams = g_slist_remove(media->streams, stream);
513 backend_stream_free(stream);
516 GSList *sipe_backend_media_get_streams(struct sipe_backend_media *media)
518 return media->streams;
521 struct sipe_backend_stream *
522 sipe_backend_media_get_stream_by_id(struct sipe_backend_media *media,
523 const gchar *id)
525 GSList *i;
526 for (i = media->streams; i; i = i->next) {
527 struct sipe_backend_stream *stream = i->data;
528 if (sipe_strequal(stream->sessionid, id))
529 return stream;
531 return NULL;
534 void
535 sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
536 struct sipe_backend_stream *stream,
537 GList *candidates)
539 GList *udp_candidates = NULL;
541 #ifndef HAVE_ICE_TCP
542 while (candidates) {
543 PurpleMediaCandidate *candidate = candidates->data;
544 PurpleMediaNetworkProtocol proto;
546 proto = purple_media_candidate_get_protocol(candidate);
547 if (proto == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP)
548 udp_candidates = g_list_append(udp_candidates, candidate);
550 candidates = candidates->next;
553 candidates = udp_candidates;
554 #endif
557 purple_media_add_remote_candidates(media->m, stream->sessionid,
558 stream->participant, candidates);
560 g_list_free(udp_candidates);
563 gboolean sipe_backend_media_is_initiator(struct sipe_backend_media *media,
564 struct sipe_backend_stream *stream)
566 return purple_media_is_initiator(media->m,
567 stream ? stream->sessionid : NULL,
568 stream ? stream->participant : NULL);
571 gboolean sipe_backend_media_accepted(struct sipe_backend_media *media)
573 return purple_media_accepted(media->m, NULL, NULL);
576 gboolean
577 sipe_backend_stream_initialized(struct sipe_backend_media *media,
578 struct sipe_backend_stream *stream)
580 g_return_val_if_fail(media, FALSE);
581 g_return_val_if_fail(stream, FALSE);
583 if (purple_media_candidates_prepared(media->m,
584 stream->sessionid,
585 stream->participant)) {
586 GList *codecs;
587 codecs = purple_media_get_codecs(media->m, stream->sessionid);
588 if (codecs) {
589 purple_media_codec_list_free(codecs);
590 return TRUE;
593 return FALSE;
596 GList *
597 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media *media,
598 struct sipe_backend_stream *stream)
600 return purple_media_get_active_local_candidates(media->m,
601 stream->sessionid,
602 stream->participant);
605 GList *
606 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media *media,
607 struct sipe_backend_stream *stream)
609 return purple_media_get_active_remote_candidates(media->m,
610 stream->sessionid,
611 stream->participant);
614 const gchar *
615 sipe_backend_stream_get_id(struct sipe_backend_stream *stream)
617 return stream->sessionid;
620 void sipe_backend_stream_hold(struct sipe_backend_media *media,
621 struct sipe_backend_stream *stream,
622 gboolean local)
624 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HOLD,
625 stream->sessionid, stream->participant,
626 local);
629 void sipe_backend_stream_unhold(struct sipe_backend_media *media,
630 struct sipe_backend_stream *stream,
631 gboolean local)
633 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_UNHOLD,
634 stream->sessionid, stream->participant,
635 local);
638 gboolean sipe_backend_stream_is_held(struct sipe_backend_stream *stream)
640 g_return_val_if_fail(stream, FALSE);
642 return stream->local_on_hold || stream->remote_on_hold;
645 struct sipe_backend_codec *
646 sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate)
648 return (struct sipe_backend_codec *)purple_media_codec_new(id, name,
649 sipe_media_to_purple(type),
650 clock_rate);
653 void
654 sipe_backend_codec_free(struct sipe_backend_codec *codec)
656 if (codec)
657 g_object_unref(codec);
661 sipe_backend_codec_get_id(struct sipe_backend_codec *codec)
663 return purple_media_codec_get_id((PurpleMediaCodec *)codec);
666 gchar *
667 sipe_backend_codec_get_name(struct sipe_backend_codec *codec)
669 /* Not explicitly documented, but return value must be g_free()'d */
670 return purple_media_codec_get_encoding_name((PurpleMediaCodec *)codec);
673 guint
674 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
676 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
679 void
680 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec,
681 const gchar *name, const gchar *value)
683 purple_media_codec_add_optional_parameter((PurpleMediaCodec *)codec, name, value);
686 GList *
687 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
689 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
692 gboolean
693 sipe_backend_set_remote_codecs(struct sipe_backend_media *media,
694 struct sipe_backend_stream *stream,
695 GList *codecs)
697 return purple_media_set_remote_codecs(media->m,
698 stream->sessionid,
699 stream->participant,
700 codecs);
703 GList*
704 sipe_backend_get_local_codecs(struct sipe_backend_media *media,
705 struct sipe_backend_stream *stream)
707 GList *codecs = purple_media_get_codecs(media->m,
708 stream->sessionid);
709 GList *i = codecs;
710 gboolean is_conference = (g_strstr_len(stream->participant,
711 strlen(stream->participant),
712 "app:conf:audio-video:") != NULL);
715 * Do not announce Theora. Its optional parameters are too long,
716 * Communicator rejects such SDP message and does not support the codec
717 * anyway.
719 * For some yet unknown reason, A/V conferencing server does not accept
720 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
721 * we are able to decode incoming SIREN from server and with MSOC
722 * client, bidirectional call using the codec works. Until resolved,
723 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
724 * seems to work properly in this scenario.
726 while (i) {
727 PurpleMediaCodec *codec = i->data;
728 gchar *encoding_name = purple_media_codec_get_encoding_name(codec);
730 if (sipe_strequal(encoding_name,"THEORA") ||
731 (is_conference && sipe_strequal(encoding_name,"SIREN"))) {
732 GList *tmp;
733 g_object_unref(codec);
734 tmp = i->next;
735 codecs = g_list_delete_link(codecs, i);
736 i = tmp;
737 } else
738 i = i->next;
740 g_free(encoding_name);
743 return codecs;
746 struct sipe_backend_candidate *
747 sipe_backend_candidate_new(const gchar *foundation,
748 SipeComponentType component,
749 SipeCandidateType type, SipeNetworkProtocol proto,
750 const gchar *ip, guint port,
751 const gchar *username,
752 const gchar *password)
754 PurpleMediaCandidate *c = purple_media_candidate_new(
755 /* Libnice and Farsight rely on non-NULL foundation to
756 * distinguish between candidates of a component. When NULL
757 * foundation is passed (ie. ICE draft 6 does not use foudation),
758 * use username instead. If no foundation is provided, Farsight
759 * may signal an active candidate different from the one actually
760 * in use. See Farsight's agent_new_selected_pair() in
761 * fs-nice-stream-transmitter.h where first candidate in the
762 * remote list is always selected when no foundation. */
763 foundation ? foundation : username,
764 component,
765 sipe_candidate_type_to_purple(type),
766 sipe_network_protocol_to_purple(proto),
768 port);
769 g_object_set(c, "username", username, "password", password, NULL);
770 return (struct sipe_backend_candidate *)c;
773 void
774 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
776 if (candidate)
777 g_object_unref(candidate);
780 gchar *
781 sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate)
783 /* Not explicitly documented, but return value must be g_free()'d */
784 return purple_media_candidate_get_username((PurpleMediaCandidate*)candidate);
787 gchar *
788 sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate)
790 /* Not explicitly documented, but return value must be g_free()'d */
791 return purple_media_candidate_get_password((PurpleMediaCandidate*)candidate);
794 gchar *
795 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate)
797 /* Not explicitly documented, but return value must be g_free()'d */
798 return purple_media_candidate_get_foundation((PurpleMediaCandidate*)candidate);
801 gchar *
802 sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate)
804 /* Not explicitly documented, but return value must be g_free()'d */
805 return purple_media_candidate_get_ip((PurpleMediaCandidate*)candidate);
808 guint
809 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
811 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
814 gchar *
815 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate)
817 /* Not explicitly documented, but return value must be g_free()'d */
818 return purple_media_candidate_get_base_ip((PurpleMediaCandidate*)candidate);
821 guint
822 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
824 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
827 guint32
828 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
830 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
833 void
834 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
836 g_object_set(candidate, "priority", priority, NULL);
839 SipeComponentType
840 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
842 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
845 SipeCandidateType
846 sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate)
848 PurpleMediaCandidateType type =
849 purple_media_candidate_get_candidate_type((PurpleMediaCandidate*)candidate);
850 return purple_candidate_type_to_sipe(type);
853 SipeNetworkProtocol
854 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate)
856 PurpleMediaNetworkProtocol proto =
857 purple_media_candidate_get_protocol((PurpleMediaCandidate*)candidate);
858 return purple_network_protocol_to_sipe(proto);
861 static void
862 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
863 gpointer value,
864 gpointer user_data)
866 GList *entry = value;
867 GList **candidates = user_data;
869 g_object_unref(entry->data);
870 *candidates = g_list_delete_link(*candidates, entry);
873 static GList *
874 ensure_candidate_pairs(GList *candidates)
876 GHashTable *lone_cand_links;
877 GList *i;
879 lone_cand_links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
881 for (i = candidates; i; i = i->next) {
882 PurpleMediaCandidate *c = i->data;
883 gchar *foundation = purple_media_candidate_get_foundation(c);
885 if (g_hash_table_lookup(lone_cand_links, foundation)) {
886 g_hash_table_remove(lone_cand_links, foundation);
887 g_free(foundation);
888 } else {
889 g_hash_table_insert(lone_cand_links, foundation, i);
893 g_hash_table_foreach(lone_cand_links, remove_lone_candidate_cb, &candidates);
894 g_hash_table_destroy(lone_cand_links);
896 return candidates;
899 GList *
900 sipe_backend_get_local_candidates(struct sipe_backend_media *media,
901 struct sipe_backend_stream *stream)
903 GList *candidates = purple_media_get_local_candidates(media->m,
904 stream->sessionid,
905 stream->participant);
907 * Sometimes purple will not return complete list of candidates, even
908 * after "candidates-prepared" signal is emitted. This is a feature of
909 * libnice, namely affecting candidates discovered via UPnP. Nice does
910 * not wait until discovery is finished and can signal end of candidate
911 * gathering before all responses from UPnP enabled gateways are received.
913 * Remove any incomplete RTP+RTCP candidate pairs from the list.
915 candidates = ensure_candidate_pairs(candidates);
916 return candidates;
919 void
920 sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local)
922 if (media)
923 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT,
924 NULL, NULL, local);
927 void
928 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
930 if (media)
931 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
932 NULL, NULL, local);
935 void
936 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
938 if (media)
939 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
940 NULL, NULL, local);
943 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
945 switch (type) {
946 case SIPE_MEDIA_AUDIO: return PURPLE_MEDIA_AUDIO;
947 case SIPE_MEDIA_VIDEO: return PURPLE_MEDIA_VIDEO;
948 default: return PURPLE_MEDIA_NONE;
952 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
954 switch (type) {
955 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
956 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
957 default: return SIPE_MEDIA_AUDIO;
961 static PurpleMediaCandidateType
962 sipe_candidate_type_to_purple(SipeCandidateType type)
964 switch (type) {
965 case SIPE_CANDIDATE_TYPE_HOST: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
966 case SIPE_CANDIDATE_TYPE_RELAY: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
967 case SIPE_CANDIDATE_TYPE_SRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
968 case SIPE_CANDIDATE_TYPE_PRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX;
969 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
973 static SipeCandidateType
974 purple_candidate_type_to_sipe(PurpleMediaCandidateType type)
976 switch (type) {
977 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: return SIPE_CANDIDATE_TYPE_HOST;
978 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: return SIPE_CANDIDATE_TYPE_RELAY;
979 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: return SIPE_CANDIDATE_TYPE_SRFLX;
980 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: return SIPE_CANDIDATE_TYPE_PRFLX;
981 default: return SIPE_CANDIDATE_TYPE_HOST;
985 static PurpleMediaNetworkProtocol
986 sipe_network_protocol_to_purple(SipeNetworkProtocol proto)
988 switch (proto) {
989 #ifdef HAVE_ICE_TCP
990 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
991 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE;
992 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
993 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE;
994 #else
995 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
996 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
997 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP;
998 #endif
999 case SIPE_NETWORK_PROTOCOL_UDP:
1000 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
1001 default:
1002 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
1006 static SipeNetworkProtocol
1007 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto)
1009 switch (proto) {
1010 #ifdef HAVE_ICE_TCP
1011 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE:
1012 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
1013 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE:
1014 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
1015 #else
1016 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP:
1017 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
1018 #endif
1019 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
1020 return SIPE_NETWORK_PROTOCOL_UDP;
1021 default:
1022 return SIPE_NETWORK_PROTOCOL_UDP;
1027 Local Variables:
1028 mode: c
1029 c-file-style: "bsd"
1030 indent-tabs-mode: t
1031 tab-width: 8
1032 End: