media: fix relay-info with Farstream 0.2
[siplcs.git] / src / purple / purple-media.c
bloba76249afb3f80ac6b8dc20d375f1c1bd2df0552e
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 #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 PurpleMedia *m;
65 GSList *streams;
66 /**
67 * Number of media streams that were not yet locally accepted or rejected.
69 guint unconfirmed_streams;
72 struct sipe_backend_stream {
73 gchar *sessionid;
74 gchar *participant;
75 gboolean local_on_hold;
76 gboolean remote_on_hold;
77 gboolean accepted;
78 gboolean initialized_cb_was_fired;
81 #if PURPLE_VERSION_CHECK(3,0,0)
82 #define SIPE_RELAYS_G_TYPE G_TYPE_PTR_ARRAY
83 #else
84 #define SIPE_RELAYS_G_TYPE G_TYPE_VALUE_ARRAY
85 #endif
87 static void
88 backend_stream_free(struct sipe_backend_stream *stream)
90 if (stream) {
91 g_free(stream->sessionid);
92 g_free(stream->participant);
93 g_free(stream);
97 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type);
98 static PurpleMediaCandidateType sipe_candidate_type_to_purple(SipeCandidateType type);
99 static SipeCandidateType purple_candidate_type_to_sipe(PurpleMediaCandidateType type);
100 static PurpleMediaNetworkProtocol sipe_network_protocol_to_purple(SipeNetworkProtocol proto);
101 static SipeNetworkProtocol purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto);
103 static void
104 maybe_signal_stream_initialized(struct sipe_media_call *call, gchar *sessionid)
106 if (call->stream_initialized_cb) {
107 struct sipe_backend_stream *stream;
108 stream = sipe_backend_media_get_stream_by_id(call->backend_private, sessionid);
110 if (sipe_backend_stream_initialized(call->backend_private, stream) &&
111 !stream->initialized_cb_was_fired) {
112 call->stream_initialized_cb(call, stream);
113 stream->initialized_cb_was_fired = TRUE;
118 static void
119 on_candidates_prepared_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
120 gchar *sessionid,
121 SIPE_UNUSED_PARAMETER gchar *participant,
122 struct sipe_media_call *call)
124 maybe_signal_stream_initialized(call, sessionid);
127 static void
128 on_codecs_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
129 gchar *sessionid,
130 struct sipe_media_call *call)
132 maybe_signal_stream_initialized(call, sessionid);
135 static void
136 on_state_changed_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
137 PurpleMediaState state,
138 gchar *sessionid,
139 gchar *participant,
140 struct sipe_media_call *call)
142 SIPE_DEBUG_INFO("sipe_media_state_changed_cb: %d %s %s\n", state, sessionid, participant);
143 if (state == PURPLE_MEDIA_STATE_END &&
144 !sessionid && !participant && call->media_end_cb)
145 call->media_end_cb(call);
148 void
149 capture_pipeline(const gchar *label) {
150 PurpleMediaManager *manager = purple_media_manager_get();
151 GstElement *pipeline = purple_media_manager_get_pipeline(manager);
152 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, label);
155 static void
156 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *message,
157 struct sipe_media_call *call)
159 capture_pipeline("ERROR");
161 if (call->error_cb)
162 call->error_cb(call, message);
165 static void
166 on_stream_info_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
167 PurpleMediaInfoType type,
168 gchar *sessionid,
169 gchar *participant,
170 gboolean local,
171 struct sipe_media_call *call)
173 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
174 if (call->call_accept_cb && !sessionid && !participant)
175 call->call_accept_cb(call, local);
176 else if (sessionid && participant) {
177 struct sipe_backend_stream *stream;
178 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
179 sessionid);
180 if (stream) {
181 if (!stream->accepted && local)
182 --call->backend_private->unconfirmed_streams;
183 stream->accepted = TRUE;
186 } else if (type == PURPLE_MEDIA_INFO_HOLD || type == PURPLE_MEDIA_INFO_UNHOLD) {
188 gboolean state = (type == PURPLE_MEDIA_INFO_HOLD);
190 if (sessionid) {
191 // Hold specific stream
192 struct sipe_backend_stream *stream;
193 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
194 sessionid);
196 if (local)
197 stream->local_on_hold = state;
198 else
199 stream->remote_on_hold = state;
200 } else {
201 // Hold all streams
202 GSList *i = sipe_backend_media_get_streams(call->backend_private);
203 for (; i; i = i->next) {
204 struct sipe_backend_stream *stream = i->data;
206 if (local)
207 stream->local_on_hold = state;
208 else
209 stream->remote_on_hold = state;
213 if (call->call_hold_cb)
214 call->call_hold_cb(call, local, state);
215 } else if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) {
216 if (!sessionid && !participant) {
217 if (type == PURPLE_MEDIA_INFO_HANGUP && call->call_hangup_cb)
218 call->call_hangup_cb(call, local);
219 else if (type == PURPLE_MEDIA_INFO_REJECT && call->call_reject_cb && !local)
220 call->call_reject_cb(call, local);
221 } else if (sessionid && participant) {
222 struct sipe_backend_stream *stream;
223 stream = sipe_backend_media_get_stream_by_id(call->backend_private,
224 sessionid);
226 if (stream) {
227 call->backend_private->streams = g_slist_remove(call->backend_private->streams, stream);
228 backend_stream_free(stream);
229 if (local && --call->backend_private->unconfirmed_streams == 0 &&
230 call->call_reject_cb)
231 call->call_reject_cb(call, local);
237 struct sipe_backend_media *
238 sipe_backend_media_new(struct sipe_core_public *sipe_public,
239 struct sipe_media_call *call,
240 const gchar *participant,
241 gboolean initiator)
243 struct sipe_backend_media *media = g_new0(struct sipe_backend_media, 1);
244 struct sipe_backend_private *purple_private = sipe_public->backend_private;
245 PurpleMediaManager *manager = purple_media_manager_get();
246 GstElement *pipeline;
248 media->m = purple_media_manager_create_media(manager,
249 purple_private->account,
250 "fsrtpconference",
251 participant, initiator);
253 g_signal_connect(G_OBJECT(media->m), "candidates-prepared",
254 G_CALLBACK(on_candidates_prepared_cb), call);
255 g_signal_connect(G_OBJECT(media->m), "codecs-changed",
256 G_CALLBACK(on_codecs_changed_cb), call);
257 g_signal_connect(G_OBJECT(media->m), "stream-info",
258 G_CALLBACK(on_stream_info_cb), call);
259 g_signal_connect(G_OBJECT(media->m), "error",
260 G_CALLBACK(on_error_cb), call);
261 g_signal_connect(G_OBJECT(media->m), "state-changed",
262 G_CALLBACK(on_state_changed_cb), call);
264 /* On error, the pipeline is no longer in PLAYING state and libpurple
265 * will not switch it back to PLAYING, preventing any more calls until
266 * application restart. We switch the state ourselves here to negate
267 * effect of any error in previous call (if any). */
268 pipeline = purple_media_manager_get_pipeline(manager);
269 gst_element_set_state(pipeline, GST_STATE_PLAYING);
271 return media;
274 void
275 sipe_backend_media_free(struct sipe_backend_media *media)
277 if (media) {
278 GSList *stream = media->streams;
280 for (; stream; stream = g_slist_delete_link(stream, stream))
281 backend_stream_free(stream->data);
283 g_free(media);
287 void
288 sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname)
290 if (media) {
291 guint num_params = 3;
292 GParameter *params = g_new0(GParameter, num_params);
293 params[0].name = "sdes-cname";
294 g_value_init(&params[0].value, G_TYPE_STRING);
295 g_value_set_string(&params[0].value, cname);
296 params[1].name = "sdes-name";
297 g_value_init(&params[1].value, G_TYPE_STRING);
298 params[2].name = "sdes-tool";
299 g_value_init(&params[2].value, G_TYPE_STRING);
301 purple_media_set_params(media->m, num_params, params);
303 g_value_unset(&params[0].value);
304 g_free(params);
308 #define FS_CODECS_CONF \
309 "# Automatically created by SIPE plugin\n" \
310 "[video/H263]\n" \
311 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
312 "\n" \
313 "[audio/PCMA]\n" \
314 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
315 "\n" \
316 "[audio/PCMU]\n" \
317 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
319 static void
320 ensure_codecs_conf()
322 gchar *filename;
323 filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
325 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
326 int fd = g_open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
327 gchar *fs_codecs_conf = FS_CODECS_CONF;
328 if ((fd < 0) || write(fd, fs_codecs_conf, strlen(fs_codecs_conf)) == -1)
329 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
330 if (fd >= 0)
331 close(fd);
334 g_free(filename);
337 static void
338 append_relay(struct sipe_backend_media_relays *relay_info, const gchar *ip,
339 guint port, gchar *type, gchar *username, gchar *password)
341 GstStructure *gst_relay_info;
343 gst_relay_info = gst_structure_new("relay-info",
344 "ip", G_TYPE_STRING, ip,
345 "port", G_TYPE_UINT, port,
346 "relay-type", G_TYPE_STRING, type,
347 "username", G_TYPE_STRING, username,
348 "password", G_TYPE_STRING, password,
349 NULL);
351 if (gst_relay_info) {
352 #if PURPLE_VERSION_CHECK(3,0,0)
353 g_ptr_array_add((GPtrArray *)relay_info, gst_relay_info);
354 #else
355 GValue value;
356 memset(&value, 0, sizeof(GValue));
357 g_value_init(&value, GST_TYPE_STRUCTURE);
358 gst_value_set_structure(&value, gst_relay_info);
360 g_value_array_append((GValueArray *)relay_info, &value);
361 gst_structure_free(gst_relay_info);
362 #endif
366 struct sipe_backend_media_relays *
367 sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password)
369 struct sipe_backend_media_relays *relay_info;
371 relay_info = (struct sipe_backend_media_relays *)
372 #if PURPLE_VERSION_CHECK(3,0,0)
373 g_ptr_array_new_with_free_func((GDestroyNotify) gst_structure_free);
374 #else
375 g_value_array_new(0);
376 #endif
378 for (; media_relays; media_relays = media_relays->next) {\
379 struct sipe_media_relay *relay = media_relays->data;
381 /* Skip relays where IP could not be resolved. */
382 if (!relay->hostname)
383 continue;
385 if (relay->udp_port != 0)
386 append_relay(relay_info, relay->hostname, relay->udp_port,
387 "udp", username, password);
389 #ifdef HAVE_ICE_TCP
390 if (relay->tcp_port != 0)
391 append_relay(relay_info, relay->hostname, relay->tcp_port,
392 "tcp", username, password);
393 #endif
396 return relay_info;
399 void
400 sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays)
402 #if !PURPLE_VERSION_CHECK(3,0,0)
403 g_value_array_free((GValueArray *)media_relays);
404 #else
405 g_ptr_array_unref((GPtrArray *)media_relays);
406 #endif
409 static guint
410 stream_demultiplex_cb(const gchar *buf, SIPE_UNUSED_PARAMETER gpointer *user_data)
412 guint8 payload_type = buf[1] & 0x7F;
413 if (payload_type >= 200 && payload_type <=204) {
414 // Looks like RTCP
415 return PURPLE_MEDIA_COMPONENT_RTCP;
416 } else {
417 // Looks like RTP
418 return PURPLE_MEDIA_COMPONENT_RTP;
422 struct sipe_backend_stream *
423 sipe_backend_media_add_stream(struct sipe_backend_media *media,
424 const gchar *id,
425 const gchar *participant,
426 SipeMediaType type,
427 SipeIceVersion ice_version,
428 gboolean initiator,
429 struct sipe_backend_media_relays *media_relays)
431 struct sipe_backend_stream *stream = NULL;
432 PurpleMediaSessionType prpl_type = sipe_media_to_purple(type);
433 GParameter *params = NULL;
434 guint params_cnt = 0;
435 gchar *transmitter;
437 if (ice_version != SIPE_ICE_NO_ICE) {
438 transmitter = "nice";
439 params_cnt = 4;
441 params = g_new0(GParameter, params_cnt);
443 params[0].name = "compatibility-mode";
444 g_value_init(&params[0].value, G_TYPE_UINT);
445 g_value_set_uint(&params[0].value,
446 ice_version == SIPE_ICE_DRAFT_6 ?
447 NICE_COMPATIBILITY_OC2007 :
448 NICE_COMPATIBILITY_OC2007R2);
450 params[1].name = "transport-protocols";
451 g_value_init(&params[1].value, G_TYPE_UINT);
452 #ifdef HAVE_ICE_TCP
453 g_value_set_uint(&params[1].value,
454 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP |
455 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE |
456 PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE);
457 #else
458 g_value_set_uint(&params[1].value,
459 PURPLE_MEDIA_NETWORK_PROTOCOL_UDP);
460 #endif
462 params[2].name = "demultiplex-func";
463 g_value_init(&params[2].value, G_TYPE_POINTER);
464 g_value_set_pointer(&params[2].value, stream_demultiplex_cb);
466 if (media_relays) {
467 params[3].name = "relay-info";
468 g_value_init(&params[3].value, SIPE_RELAYS_G_TYPE);
469 g_value_set_boxed(&params[3].value, media_relays);
470 } else
471 --params_cnt;
472 } else {
473 // TODO: session naming here, Communicator needs audio/video
474 transmitter = "rawudp";
475 //sessionid = "sipe-voice-rawudp";
477 /* To avoid Coverity FORWARD_NULL warning. params_cnt is
478 * still 0 so this is a no-op. libpurple API documentation
479 * doesn't specify if params can be NULL or not. */
480 params = g_new0(GParameter, 1);
483 ensure_codecs_conf();
485 if (purple_media_add_stream(media->m, id, participant, prpl_type,
486 initiator, transmitter, params_cnt,
487 params)) {
488 stream = g_new0(struct sipe_backend_stream, 1);
489 stream->sessionid = g_strdup(id);
490 stream->participant = g_strdup(participant);
491 stream->initialized_cb_was_fired = FALSE;
493 media->streams = g_slist_append(media->streams, stream);
494 if (!initiator)
495 ++media->unconfirmed_streams;
498 if ((params_cnt > 2) && media_relays)
499 g_value_unset(&params[3].value);
501 g_free(params);
503 return stream;
506 void
507 sipe_backend_media_remove_stream(struct sipe_backend_media *media,
508 struct sipe_backend_stream *stream)
510 g_return_if_fail(media && stream);
512 purple_media_end(media->m, stream->sessionid, NULL);
513 media->streams = g_slist_remove(media->streams, stream);
514 backend_stream_free(stream);
517 GSList *sipe_backend_media_get_streams(struct sipe_backend_media *media)
519 return media->streams;
522 struct sipe_backend_stream *
523 sipe_backend_media_get_stream_by_id(struct sipe_backend_media *media,
524 const gchar *id)
526 GSList *i;
527 for (i = media->streams; i; i = i->next) {
528 struct sipe_backend_stream *stream = i->data;
529 if (sipe_strequal(stream->sessionid, id))
530 return stream;
532 return NULL;
535 void
536 sipe_backend_media_add_remote_candidates(struct sipe_backend_media *media,
537 struct sipe_backend_stream *stream,
538 GList *candidates)
540 GList *udp_candidates = NULL;
542 #ifndef HAVE_ICE_TCP
543 while (candidates) {
544 PurpleMediaCandidate *candidate = candidates->data;
545 PurpleMediaNetworkProtocol proto;
547 proto = purple_media_candidate_get_protocol(candidate);
548 if (proto == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP)
549 udp_candidates = g_list_append(udp_candidates, candidate);
551 candidates = candidates->next;
554 candidates = udp_candidates;
555 #endif
558 purple_media_add_remote_candidates(media->m, stream->sessionid,
559 stream->participant, candidates);
561 g_list_free(udp_candidates);
564 gboolean sipe_backend_media_is_initiator(struct sipe_backend_media *media,
565 struct sipe_backend_stream *stream)
567 return purple_media_is_initiator(media->m,
568 stream ? stream->sessionid : NULL,
569 stream ? stream->participant : NULL);
572 gboolean sipe_backend_media_accepted(struct sipe_backend_media *media)
574 return purple_media_accepted(media->m, NULL, NULL);
577 gboolean
578 sipe_backend_stream_initialized(struct sipe_backend_media *media,
579 struct sipe_backend_stream *stream)
581 g_return_val_if_fail(media, FALSE);
582 g_return_val_if_fail(stream, FALSE);
584 if (purple_media_candidates_prepared(media->m,
585 stream->sessionid,
586 stream->participant)) {
587 GList *codecs;
588 codecs = purple_media_get_codecs(media->m, stream->sessionid);
589 if (codecs) {
590 purple_media_codec_list_free(codecs);
591 return TRUE;
594 return FALSE;
597 GList *
598 sipe_backend_media_get_active_local_candidates(struct sipe_backend_media *media,
599 struct sipe_backend_stream *stream)
601 return purple_media_get_active_local_candidates(media->m,
602 stream->sessionid,
603 stream->participant);
606 GList *
607 sipe_backend_media_get_active_remote_candidates(struct sipe_backend_media *media,
608 struct sipe_backend_stream *stream)
610 return purple_media_get_active_remote_candidates(media->m,
611 stream->sessionid,
612 stream->participant);
615 const gchar *
616 sipe_backend_stream_get_id(struct sipe_backend_stream *stream)
618 return stream->sessionid;
621 void sipe_backend_stream_hold(struct sipe_backend_media *media,
622 struct sipe_backend_stream *stream,
623 gboolean local)
625 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HOLD,
626 stream->sessionid, stream->participant,
627 local);
630 void sipe_backend_stream_unhold(struct sipe_backend_media *media,
631 struct sipe_backend_stream *stream,
632 gboolean local)
634 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_UNHOLD,
635 stream->sessionid, stream->participant,
636 local);
639 gboolean sipe_backend_stream_is_held(struct sipe_backend_stream *stream)
641 g_return_val_if_fail(stream, FALSE);
643 return stream->local_on_hold || stream->remote_on_hold;
646 struct sipe_backend_codec *
647 sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate)
649 return (struct sipe_backend_codec *)purple_media_codec_new(id, name,
650 sipe_media_to_purple(type),
651 clock_rate);
654 void
655 sipe_backend_codec_free(struct sipe_backend_codec *codec)
657 if (codec)
658 g_object_unref(codec);
662 sipe_backend_codec_get_id(struct sipe_backend_codec *codec)
664 return purple_media_codec_get_id((PurpleMediaCodec *)codec);
667 gchar *
668 sipe_backend_codec_get_name(struct sipe_backend_codec *codec)
670 /* Not explicitly documented, but return value must be g_free()'d */
671 return purple_media_codec_get_encoding_name((PurpleMediaCodec *)codec);
674 guint
675 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
677 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
680 void
681 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec,
682 const gchar *name, const gchar *value)
684 purple_media_codec_add_optional_parameter((PurpleMediaCodec *)codec, name, value);
687 GList *
688 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
690 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
693 gboolean
694 sipe_backend_set_remote_codecs(struct sipe_backend_media *media,
695 struct sipe_backend_stream *stream,
696 GList *codecs)
698 return purple_media_set_remote_codecs(media->m,
699 stream->sessionid,
700 stream->participant,
701 codecs);
704 GList*
705 sipe_backend_get_local_codecs(struct sipe_backend_media *media,
706 struct sipe_backend_stream *stream)
708 GList *codecs = purple_media_get_codecs(media->m,
709 stream->sessionid);
710 GList *i = codecs;
711 gboolean is_conference = (g_strstr_len(stream->participant,
712 strlen(stream->participant),
713 "app:conf:audio-video:") != NULL);
716 * Do not announce Theora. Its optional parameters are too long,
717 * Communicator rejects such SDP message and does not support the codec
718 * anyway.
720 * For some yet unknown reason, A/V conferencing server does not accept
721 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
722 * we are able to decode incoming SIREN from server and with MSOC
723 * client, bidirectional call using the codec works. Until resolved,
724 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
725 * seems to work properly in this scenario.
727 while (i) {
728 PurpleMediaCodec *codec = i->data;
729 gchar *encoding_name = purple_media_codec_get_encoding_name(codec);
731 if (sipe_strequal(encoding_name,"THEORA") ||
732 (is_conference && sipe_strequal(encoding_name,"SIREN"))) {
733 GList *tmp;
734 g_object_unref(codec);
735 tmp = i->next;
736 codecs = g_list_delete_link(codecs, i);
737 i = tmp;
738 } else
739 i = i->next;
741 g_free(encoding_name);
744 return codecs;
747 struct sipe_backend_candidate *
748 sipe_backend_candidate_new(const gchar *foundation,
749 SipeComponentType component,
750 SipeCandidateType type, SipeNetworkProtocol proto,
751 const gchar *ip, guint port,
752 const gchar *username,
753 const gchar *password)
755 PurpleMediaCandidate *c = purple_media_candidate_new(
756 /* Libnice and Farsight rely on non-NULL foundation to
757 * distinguish between candidates of a component. When NULL
758 * foundation is passed (ie. ICE draft 6 does not use foudation),
759 * use username instead. If no foundation is provided, Farsight
760 * may signal an active candidate different from the one actually
761 * in use. See Farsight's agent_new_selected_pair() in
762 * fs-nice-stream-transmitter.h where first candidate in the
763 * remote list is always selected when no foundation. */
764 foundation ? foundation : username,
765 component,
766 sipe_candidate_type_to_purple(type),
767 sipe_network_protocol_to_purple(proto),
769 port);
770 g_object_set(c, "username", username, "password", password, NULL);
771 return (struct sipe_backend_candidate *)c;
774 void
775 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
777 if (candidate)
778 g_object_unref(candidate);
781 gchar *
782 sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate)
784 /* Not explicitly documented, but return value must be g_free()'d */
785 return purple_media_candidate_get_username((PurpleMediaCandidate*)candidate);
788 gchar *
789 sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate)
791 /* Not explicitly documented, but return value must be g_free()'d */
792 return purple_media_candidate_get_password((PurpleMediaCandidate*)candidate);
795 gchar *
796 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate)
798 /* Not explicitly documented, but return value must be g_free()'d */
799 return purple_media_candidate_get_foundation((PurpleMediaCandidate*)candidate);
802 gchar *
803 sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate)
805 /* Not explicitly documented, but return value must be g_free()'d */
806 return purple_media_candidate_get_ip((PurpleMediaCandidate*)candidate);
809 guint
810 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
812 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
815 gchar *
816 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate)
818 /* Not explicitly documented, but return value must be g_free()'d */
819 return purple_media_candidate_get_base_ip((PurpleMediaCandidate*)candidate);
822 guint
823 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
825 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
828 guint32
829 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
831 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
834 void
835 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
837 g_object_set(candidate, "priority", priority, NULL);
840 SipeComponentType
841 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
843 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
846 SipeCandidateType
847 sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate)
849 PurpleMediaCandidateType type =
850 purple_media_candidate_get_candidate_type((PurpleMediaCandidate*)candidate);
851 return purple_candidate_type_to_sipe(type);
854 SipeNetworkProtocol
855 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate)
857 PurpleMediaNetworkProtocol proto =
858 purple_media_candidate_get_protocol((PurpleMediaCandidate*)candidate);
859 return purple_network_protocol_to_sipe(proto);
862 static void
863 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
864 gpointer value,
865 gpointer user_data)
867 GList *entry = value;
868 GList **candidates = user_data;
870 g_object_unref(entry->data);
871 *candidates = g_list_delete_link(*candidates, entry);
874 static GList *
875 ensure_candidate_pairs(GList *candidates)
877 GHashTable *lone_cand_links;
878 GList *i;
880 lone_cand_links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
882 for (i = candidates; i; i = i->next) {
883 PurpleMediaCandidate *c = i->data;
884 gchar *foundation = purple_media_candidate_get_foundation(c);
886 if (g_hash_table_lookup(lone_cand_links, foundation)) {
887 g_hash_table_remove(lone_cand_links, foundation);
888 g_free(foundation);
889 } else {
890 g_hash_table_insert(lone_cand_links, foundation, i);
894 g_hash_table_foreach(lone_cand_links, remove_lone_candidate_cb, &candidates);
895 g_hash_table_destroy(lone_cand_links);
897 return candidates;
900 GList *
901 sipe_backend_get_local_candidates(struct sipe_backend_media *media,
902 struct sipe_backend_stream *stream)
904 GList *candidates = purple_media_get_local_candidates(media->m,
905 stream->sessionid,
906 stream->participant);
908 * Sometimes purple will not return complete list of candidates, even
909 * after "candidates-prepared" signal is emitted. This is a feature of
910 * libnice, namely affecting candidates discovered via UPnP. Nice does
911 * not wait until discovery is finished and can signal end of candidate
912 * gathering before all responses from UPnP enabled gateways are received.
914 * Remove any incomplete RTP+RTCP candidate pairs from the list.
916 candidates = ensure_candidate_pairs(candidates);
917 return candidates;
920 void
921 sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local)
923 if (media)
924 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT,
925 NULL, NULL, local);
928 void
929 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
931 if (media)
932 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
933 NULL, NULL, local);
936 void
937 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
939 if (media)
940 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
941 NULL, NULL, local);
944 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
946 switch (type) {
947 case SIPE_MEDIA_AUDIO: return PURPLE_MEDIA_AUDIO;
948 case SIPE_MEDIA_VIDEO: return PURPLE_MEDIA_VIDEO;
949 default: return PURPLE_MEDIA_NONE;
953 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
955 switch (type) {
956 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
957 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
958 default: return SIPE_MEDIA_AUDIO;
962 static PurpleMediaCandidateType
963 sipe_candidate_type_to_purple(SipeCandidateType type)
965 switch (type) {
966 case SIPE_CANDIDATE_TYPE_HOST: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
967 case SIPE_CANDIDATE_TYPE_RELAY: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
968 case SIPE_CANDIDATE_TYPE_SRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
969 case SIPE_CANDIDATE_TYPE_PRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX;
970 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
974 static SipeCandidateType
975 purple_candidate_type_to_sipe(PurpleMediaCandidateType type)
977 switch (type) {
978 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: return SIPE_CANDIDATE_TYPE_HOST;
979 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: return SIPE_CANDIDATE_TYPE_RELAY;
980 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: return SIPE_CANDIDATE_TYPE_SRFLX;
981 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: return SIPE_CANDIDATE_TYPE_PRFLX;
982 default: return SIPE_CANDIDATE_TYPE_HOST;
986 static PurpleMediaNetworkProtocol
987 sipe_network_protocol_to_purple(SipeNetworkProtocol proto)
989 switch (proto) {
990 #ifdef HAVE_ICE_TCP
991 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
992 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE;
993 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
994 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE;
995 #else
996 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
997 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
998 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP;
999 #endif
1000 case SIPE_NETWORK_PROTOCOL_UDP:
1001 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
1002 default:
1003 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
1007 static SipeNetworkProtocol
1008 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto)
1010 switch (proto) {
1011 #ifdef HAVE_ICE_TCP
1012 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE:
1013 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
1014 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE:
1015 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
1016 #else
1017 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP:
1018 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
1019 #endif
1020 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
1021 return SIPE_NETWORK_PROTOCOL_UDP;
1022 default:
1023 return SIPE_NETWORK_PROTOCOL_UDP;
1028 Local Variables:
1029 mode: c
1030 c-file-style: "bsd"
1031 indent-tabs-mode: t
1032 tab-width: 8
1033 End: