configure: add check for new purple TCP enums
[siplcs.git] / src / purple / purple-media.c
blobad96083ec6d7b6dd2897ee6630194fd2805d8c0e
1 /**
2 * @file purple-media.c
4 * pidgin-sipe
6 * Copyright (C) 2010-2015 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__)
56 #if ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 2)) || (__GNUC__ >= 5)
57 #if defined(__ARMEL__) || defined(__ARMEB__) || defined(__mips__) || defined(__sparc__) || (defined(__powerpc__) && defined(__NO_FPRS__))
58 #pragma GCC diagnostic ignored "-Wcast-align"
59 #endif
60 #endif
61 #endif
63 #include "media-gst.h"
65 struct sipe_backend_media {
66 PurpleMedia *m;
67 /**
68 * Number of media streams that were not yet locally accepted or rejected.
70 guint unconfirmed_streams;
73 struct sipe_backend_media_stream {
74 gboolean local_on_hold;
75 gboolean remote_on_hold;
76 gboolean accepted;
77 gboolean initialized_cb_was_fired;
80 #if PURPLE_VERSION_CHECK(3,0,0)
81 #define SIPE_RELAYS_G_TYPE G_TYPE_PTR_ARRAY
82 #else
83 #define SIPE_RELAYS_G_TYPE G_TYPE_VALUE_ARRAY
84 #endif
86 void
87 sipe_backend_media_stream_free(struct sipe_backend_media_stream *stream)
89 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_media_stream *stream;
103 stream = sipe_core_media_get_stream_by_id(call, sessionid);
105 if (sipe_backend_stream_initialized(call, stream) &&
106 !stream->backend_private->initialized_cb_was_fired) {
107 call->stream_initialized_cb(call, stream);
108 stream->backend_private->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 if (sessionid && participant) {
140 struct sipe_media_stream *stream =
141 sipe_core_media_get_stream_by_id(call, sessionid);
142 if (stream) {
143 call->stream_end_cb(call, stream);
145 } else if (!sessionid && !participant && call->media_end_cb) {
146 call->media_end_cb(call);
151 void
152 capture_pipeline(const gchar *label) {
153 PurpleMediaManager *manager = purple_media_manager_get();
154 GstElement *pipeline = purple_media_manager_get_pipeline(manager);
155 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(pipeline), GST_DEBUG_GRAPH_SHOW_ALL, label);
158 static void
159 on_error_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media, gchar *message,
160 struct sipe_media_call *call)
162 capture_pipeline("ERROR");
164 if (call->error_cb)
165 call->error_cb(call, message);
168 static void
169 on_stream_info_cb(PurpleMedia *media,
170 PurpleMediaInfoType type,
171 gchar *sessionid,
172 gchar *participant,
173 gboolean local,
174 struct sipe_media_call *call)
176 if (type == PURPLE_MEDIA_INFO_ACCEPT) {
177 if (call->call_accept_cb && !sessionid && !participant)
178 call->call_accept_cb(call, local);
179 else if (sessionid && participant) {
180 struct sipe_media_stream *stream;
181 stream = sipe_core_media_get_stream_by_id(call, sessionid);
182 if (stream) {
183 if (!stream->backend_private->accepted && local)
184 --call->backend_private->unconfirmed_streams;
185 stream->backend_private->accepted = TRUE;
188 } else if (type == PURPLE_MEDIA_INFO_HOLD || type == PURPLE_MEDIA_INFO_UNHOLD) {
190 gboolean state = (type == PURPLE_MEDIA_INFO_HOLD);
192 if (sessionid) {
193 // Hold specific stream
194 struct sipe_media_stream *stream;
195 stream = sipe_core_media_get_stream_by_id(call, sessionid);
197 if (local)
198 stream->backend_private->local_on_hold = state;
199 else
200 stream->backend_private->remote_on_hold = state;
201 } else {
202 // Hold all streams
203 GList *session_ids = purple_media_get_session_ids(media);
205 for (; session_ids; session_ids = session_ids->next) {
206 struct sipe_media_stream *stream =
207 sipe_core_media_get_stream_by_id(call, session_ids->data);
209 if (local)
210 stream->backend_private->local_on_hold = state;
211 else
212 stream->backend_private->remote_on_hold = state;
215 g_list_free(session_ids);
218 if (call->call_hold_cb)
219 call->call_hold_cb(call, local, state);
220 } else if (type == PURPLE_MEDIA_INFO_HANGUP || type == PURPLE_MEDIA_INFO_REJECT) {
221 if (!sessionid && !participant) {
222 if (type == PURPLE_MEDIA_INFO_HANGUP && call->call_hangup_cb)
223 call->call_hangup_cb(call, local);
224 else if (type == PURPLE_MEDIA_INFO_REJECT && call->call_reject_cb && !local)
225 call->call_reject_cb(call, local);
226 } else if (sessionid && participant) {
227 struct sipe_media_stream *stream;
228 stream = sipe_core_media_get_stream_by_id(call, sessionid);
230 if (stream) {
231 if (local && --call->backend_private->unconfirmed_streams == 0 &&
232 call->call_reject_cb)
233 call->call_reject_cb(call, local);
239 static void
240 on_candidate_pair_established_cb(SIPE_UNUSED_PARAMETER PurpleMedia *media,
241 SIPE_UNUSED_PARAMETER const gchar *sessionid,
242 SIPE_UNUSED_PARAMETER const gchar *participant,
243 SIPE_UNUSED_PARAMETER PurpleMediaCandidate *local_candidate,
244 SIPE_UNUSED_PARAMETER PurpleMediaCandidate *remote_candidate,
245 SIPE_UNUSED_PARAMETER struct sipe_media_call *call)
247 #ifdef HAVE_PURPLE_NEW_TCP_ENUMS
248 if (purple_media_candidate_get_protocol(local_candidate) != PURPLE_MEDIA_NETWORK_PROTOCOL_UDP) {
249 purple_media_set_send_rtcp_mux(media, sessionid, participant, TRUE);
251 #endif
254 struct sipe_backend_media *
255 sipe_backend_media_new(struct sipe_core_public *sipe_public,
256 struct sipe_media_call *call,
257 const gchar *participant,
258 gboolean initiator)
260 struct sipe_backend_media *media = g_new0(struct sipe_backend_media, 1);
261 struct sipe_backend_private *purple_private = sipe_public->backend_private;
262 PurpleMediaManager *manager = purple_media_manager_get();
263 GstElement *pipeline;
265 media->m = purple_media_manager_create_media(manager,
266 purple_private->account,
267 "fsrtpconference",
268 participant, initiator);
270 g_signal_connect(G_OBJECT(media->m), "candidates-prepared",
271 G_CALLBACK(on_candidates_prepared_cb), call);
272 g_signal_connect(G_OBJECT(media->m), "codecs-changed",
273 G_CALLBACK(on_codecs_changed_cb), call);
274 g_signal_connect(G_OBJECT(media->m), "stream-info",
275 G_CALLBACK(on_stream_info_cb), call);
276 g_signal_connect(G_OBJECT(media->m), "error",
277 G_CALLBACK(on_error_cb), call);
278 g_signal_connect(G_OBJECT(media->m), "state-changed",
279 G_CALLBACK(on_state_changed_cb), call);
280 g_signal_connect(G_OBJECT(media->m), "candidate-pair-established",
281 G_CALLBACK(on_candidate_pair_established_cb), call);
284 /* On error, the pipeline is no longer in PLAYING state and libpurple
285 * will not switch it back to PLAYING, preventing any more calls until
286 * application restart. We switch the state ourselves here to negate
287 * effect of any error in previous call (if any). */
288 pipeline = purple_media_manager_get_pipeline(manager);
289 gst_element_set_state(pipeline, GST_STATE_PLAYING);
291 return media;
294 void
295 sipe_backend_media_free(struct sipe_backend_media *media)
297 g_free(media);
300 void
301 sipe_backend_media_set_cname(struct sipe_backend_media *media, gchar *cname)
303 if (media) {
304 guint num_params = 3;
305 GParameter *params = g_new0(GParameter, num_params);
306 params[0].name = "sdes-cname";
307 g_value_init(&params[0].value, G_TYPE_STRING);
308 g_value_set_string(&params[0].value, cname);
309 params[1].name = "sdes-name";
310 g_value_init(&params[1].value, G_TYPE_STRING);
311 params[2].name = "sdes-tool";
312 g_value_init(&params[2].value, G_TYPE_STRING);
314 purple_media_set_params(media->m, num_params, params);
316 g_value_unset(&params[0].value);
317 g_free(params);
321 #define FS_CODECS_CONF \
322 "# Automatically created by SIPE plugin\n" \
323 "[video/H263]\n" \
324 "farsight-send-profile=videoscale ! ffmpegcolorspace ! fsvideoanyrate ! ffenc_h263 rtp-payload-size=30 ! rtph263pay\n" \
325 "\n" \
326 "[audio/PCMA]\n" \
327 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! alawenc ! rtppcmapay min-ptime=20000000 max-ptime=20000000\n" \
328 "\n" \
329 "[audio/PCMU]\n" \
330 "farsight-send-profile=audioconvert ! audioresample ! audioconvert ! mulawenc ! rtppcmupay min-ptime=20000000 max-ptime=20000000\n";
332 static void
333 ensure_codecs_conf()
335 gchar *filename;
336 filename = g_build_filename(purple_user_dir(), "fs-codec.conf", NULL);
338 if (!g_file_test(filename, G_FILE_TEST_EXISTS)) {
339 int fd = g_open(filename, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
340 gchar *fs_codecs_conf = FS_CODECS_CONF;
341 if ((fd < 0) || write(fd, fs_codecs_conf, strlen(fs_codecs_conf)) == -1)
342 SIPE_DEBUG_ERROR_NOFORMAT("Can not create fs-codec.conf!");
343 if (fd >= 0)
344 close(fd);
347 g_free(filename);
350 static void
351 append_relay(struct sipe_backend_media_relays *relay_info, const gchar *ip,
352 guint port, gchar *type, gchar *username, gchar *password)
354 GstStructure *gst_relay_info;
356 gst_relay_info = gst_structure_new("relay-info",
357 "ip", G_TYPE_STRING, ip,
358 "port", G_TYPE_UINT, port,
359 "relay-type", G_TYPE_STRING, type,
360 "username", G_TYPE_STRING, username,
361 "password", G_TYPE_STRING, password,
362 NULL);
364 if (gst_relay_info) {
365 #if PURPLE_VERSION_CHECK(3,0,0)
366 g_ptr_array_add((GPtrArray *)relay_info, gst_relay_info);
367 #else
368 GValue value;
369 memset(&value, 0, sizeof(GValue));
370 g_value_init(&value, GST_TYPE_STRUCTURE);
371 gst_value_set_structure(&value, gst_relay_info);
373 g_value_array_append((GValueArray *)relay_info, &value);
374 gst_structure_free(gst_relay_info);
375 #endif
379 struct sipe_backend_media_relays *
380 sipe_backend_media_relays_convert(GSList *media_relays, gchar *username, gchar *password)
382 struct sipe_backend_media_relays *relay_info;
384 relay_info = (struct sipe_backend_media_relays *)
385 #if PURPLE_VERSION_CHECK(3,0,0)
386 g_ptr_array_new_with_free_func((GDestroyNotify) gst_structure_free);
387 #else
388 g_value_array_new(0);
389 #endif
391 for (; media_relays; media_relays = media_relays->next) {\
392 struct sipe_media_relay *relay = media_relays->data;
394 /* Skip relays where IP could not be resolved. */
395 if (!relay->hostname)
396 continue;
398 if (relay->udp_port != 0)
399 append_relay(relay_info, relay->hostname, relay->udp_port,
400 "udp", username, password);
402 #ifdef HAVE_PURPLE_NEW_TCP_ENUMS
403 if (relay->tcp_port != 0) {
404 gchar *type = "tcp";
405 if (relay->tcp_port == 443)
406 type = "tls";
407 append_relay(relay_info, relay->hostname, relay->tcp_port,
408 type, username, password);
410 #endif
413 return relay_info;
416 void
417 sipe_backend_media_relays_free(struct sipe_backend_media_relays *media_relays)
419 #if !PURPLE_VERSION_CHECK(3,0,0)
420 g_value_array_free((GValueArray *)media_relays);
421 #else
422 g_ptr_array_unref((GPtrArray *)media_relays);
423 #endif
426 struct sipe_backend_media_stream *
427 sipe_backend_media_add_stream(struct sipe_media_call *call,
428 const gchar *id,
429 const gchar *participant,
430 SipeMediaType type,
431 SipeIceVersion ice_version,
432 gboolean initiator,
433 struct sipe_backend_media_relays *media_relays)
435 struct sipe_backend_media *media = call->backend_private;
436 struct sipe_backend_media_stream *stream = NULL;
437 PurpleMediaSessionType prpl_type = sipe_media_to_purple(type);
438 // Preallocate enough space for all potential parameters to fit.
439 GParameter *params = g_new0(GParameter, 5);
440 guint params_cnt = 0;
441 gchar *transmitter;
442 GValue *relay_info = NULL;
444 if (ice_version != SIPE_ICE_NO_ICE) {
445 transmitter = "nice";
447 params[params_cnt].name = "compatibility-mode";
448 g_value_init(&params[params_cnt].value, G_TYPE_UINT);
449 g_value_set_uint(&params[params_cnt].value,
450 ice_version == SIPE_ICE_DRAFT_6 ?
451 NICE_COMPATIBILITY_OC2007 :
452 NICE_COMPATIBILITY_OC2007R2);
453 ++params_cnt;
455 if (media_relays) {
456 params[params_cnt].name = "relay-info";
457 g_value_init(&params[params_cnt].value, SIPE_RELAYS_G_TYPE);
458 g_value_set_boxed(&params[params_cnt].value, media_relays);
459 relay_info = &params[params_cnt].value;
460 ++params_cnt;
462 } else {
463 // TODO: session naming here, Communicator needs audio/video
464 transmitter = "rawudp";
465 //sessionid = "sipe-voice-rawudp";
468 ensure_codecs_conf();
470 if (purple_media_add_stream(media->m, id, participant, prpl_type,
471 initiator, transmitter, params_cnt,
472 params)) {
473 stream = g_new0(struct sipe_backend_media_stream, 1);
474 stream->initialized_cb_was_fired = FALSE;
476 if (!initiator)
477 ++media->unconfirmed_streams;
480 if (relay_info) {
481 g_value_unset(relay_info);
484 g_free(params);
486 return stream;
489 void
490 sipe_backend_media_stream_end(struct sipe_media_call *media,
491 struct sipe_media_stream *stream)
493 purple_media_end(media->backend_private->m, stream->id, NULL);
496 void
497 sipe_backend_media_add_remote_candidates(struct sipe_media_call *media,
498 struct sipe_media_stream *stream,
499 GList *candidates)
501 GList *udp_candidates = NULL;
503 #ifdef HAVE_PURPLE_NEW_TCP_ENUMS
504 while (candidates) {
505 PurpleMediaCandidate *candidate = candidates->data;
506 PurpleMediaNetworkProtocol proto;
508 proto = purple_media_candidate_get_protocol(candidate);
509 if (proto == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP)
510 udp_candidates = g_list_append(udp_candidates, candidate);
512 candidates = candidates->next;
515 candidates = udp_candidates;
516 #endif
518 purple_media_add_remote_candidates(media->backend_private->m,
519 stream->id, media->with, candidates);
521 g_list_free(udp_candidates);
524 gboolean sipe_backend_media_is_initiator(struct sipe_media_call *media,
525 struct sipe_media_stream *stream)
527 return purple_media_is_initiator(media->backend_private->m,
528 stream ? stream->id : NULL,
529 stream ? media->with : NULL);
532 gboolean sipe_backend_media_accepted(struct sipe_backend_media *media)
534 return purple_media_accepted(media->m, NULL, NULL);
537 gboolean
538 sipe_backend_stream_initialized(struct sipe_media_call *media,
539 struct sipe_media_stream *stream)
541 g_return_val_if_fail(media, FALSE);
542 g_return_val_if_fail(stream, FALSE);
544 if (purple_media_candidates_prepared(media->backend_private->m,
545 stream->id, media->with)) {
546 GList *codecs;
547 codecs = purple_media_get_codecs(media->backend_private->m,
548 stream->id);
549 if (codecs) {
550 purple_media_codec_list_free(codecs);
551 return TRUE;
554 return FALSE;
557 static GList *
558 duplicate_tcp_candidates(GList *candidates)
560 GList *i;
561 GList *result = NULL;
563 for (i = candidates; i; i = i->next) {
564 PurpleMediaCandidate *candidate = i->data;
565 PurpleMediaNetworkProtocol protocol =
566 purple_media_candidate_get_protocol(candidate);
567 guint component_id =
568 purple_media_candidate_get_component_id(candidate);
570 if (protocol != PURPLE_MEDIA_NETWORK_PROTOCOL_UDP) {
571 PurpleMediaCandidate *c2;
573 if (component_id != PURPLE_MEDIA_COMPONENT_RTP) {
574 /* Ignore TCP candidates for other than
575 * the first component. */
576 g_object_unref(candidate);
577 continue;
580 c2 = purple_media_candidate_copy(candidate);
581 g_object_set(c2,
582 "component-id", PURPLE_MEDIA_COMPONENT_RTCP,
583 NULL);
584 result = g_list_append(result, c2);
587 result = g_list_append(result, candidate);
590 g_list_free(candidates);
592 return result;
595 GList *
596 sipe_backend_media_get_active_local_candidates(struct sipe_media_call *media,
597 struct sipe_media_stream *stream)
599 GList *candidates = purple_media_get_active_local_candidates(
600 media->backend_private->m, stream->id, media->with);
601 return duplicate_tcp_candidates(candidates);
604 GList *
605 sipe_backend_media_get_active_remote_candidates(struct sipe_media_call *media,
606 struct sipe_media_stream *stream)
608 GList *candidates = purple_media_get_active_remote_candidates(
609 media->backend_private->m, stream->id, media->with);
610 return duplicate_tcp_candidates(candidates);
613 #ifdef HAVE_SRTP
614 void
615 sipe_backend_media_set_encryption_keys(struct sipe_media_call *media,
616 struct sipe_media_stream *stream,
617 const guchar *encryption_key,
618 const guchar *decryption_key)
620 purple_media_set_encryption_parameters(media->backend_private->m,
621 stream->id,
622 "aes-128-icm",
623 "hmac-sha1-80",
624 (gchar *)encryption_key, SIPE_SRTP_KEY_LEN);
625 purple_media_set_decryption_parameters(media->backend_private->m,
626 stream->id, media->with,
627 "aes-128-icm",
628 "hmac-sha1-80",
629 (gchar *)decryption_key, SIPE_SRTP_KEY_LEN);
631 #else
632 void
633 sipe_backend_media_set_encryption_keys(SIPE_UNUSED_PARAMETER struct sipe_media_call *media,
634 SIPE_UNUSED_PARAMETER struct sipe_media_stream *stream,
635 SIPE_UNUSED_PARAMETER const guchar *encryption_key,
636 SIPE_UNUSED_PARAMETER const guchar *decryption_key)
638 #endif
640 void sipe_backend_stream_hold(struct sipe_media_call *media,
641 struct sipe_media_stream *stream,
642 gboolean local)
644 purple_media_stream_info(media->backend_private->m, PURPLE_MEDIA_INFO_HOLD,
645 stream->id, media->with, local);
648 void sipe_backend_stream_unhold(struct sipe_media_call *media,
649 struct sipe_media_stream *stream,
650 gboolean local)
652 purple_media_stream_info(media->backend_private->m, PURPLE_MEDIA_INFO_UNHOLD,
653 stream->id, media->with, local);
656 gboolean sipe_backend_stream_is_held(struct sipe_media_stream *stream)
658 g_return_val_if_fail(stream, FALSE);
660 return stream->backend_private->local_on_hold ||
661 stream->backend_private->remote_on_hold;
664 struct sipe_backend_codec *
665 sipe_backend_codec_new(int id, const char *name, SipeMediaType type, guint clock_rate)
667 return (struct sipe_backend_codec *)purple_media_codec_new(id, name,
668 sipe_media_to_purple(type),
669 clock_rate);
672 void
673 sipe_backend_codec_free(struct sipe_backend_codec *codec)
675 if (codec)
676 g_object_unref(codec);
680 sipe_backend_codec_get_id(struct sipe_backend_codec *codec)
682 return purple_media_codec_get_id((PurpleMediaCodec *)codec);
685 gchar *
686 sipe_backend_codec_get_name(struct sipe_backend_codec *codec)
688 /* Not explicitly documented, but return value must be g_free()'d */
689 return purple_media_codec_get_encoding_name((PurpleMediaCodec *)codec);
692 guint
693 sipe_backend_codec_get_clock_rate(struct sipe_backend_codec *codec)
695 return purple_media_codec_get_clock_rate((PurpleMediaCodec *)codec);
698 void
699 sipe_backend_codec_add_optional_parameter(struct sipe_backend_codec *codec,
700 const gchar *name, const gchar *value)
702 purple_media_codec_add_optional_parameter((PurpleMediaCodec *)codec, name, value);
705 GList *
706 sipe_backend_codec_get_optional_parameters(struct sipe_backend_codec *codec)
708 return purple_media_codec_get_optional_parameters((PurpleMediaCodec *)codec);
711 gboolean
712 sipe_backend_set_remote_codecs(struct sipe_media_call *media,
713 struct sipe_media_stream *stream,
714 GList *codecs)
716 return purple_media_set_remote_codecs(media->backend_private->m,
717 stream->id, media->with, codecs);
720 GList*
721 sipe_backend_get_local_codecs(struct sipe_media_call *media,
722 struct sipe_media_stream *stream)
724 GList *codecs = purple_media_get_codecs(media->backend_private->m,
725 stream->id);
726 GList *i = codecs;
727 gboolean is_conference = (g_strstr_len(media->with, strlen(media->with),
728 "app:conf:audio-video:") != NULL);
731 * Do not announce Theora. Its optional parameters are too long,
732 * Communicator rejects such SDP message and does not support the codec
733 * anyway.
735 * For some yet unknown reason, A/V conferencing server does not accept
736 * voice stream sent by SIPE when SIREN codec is in use. Nevertheless,
737 * we are able to decode incoming SIREN from server and with MSOC
738 * client, bidirectional call using the codec works. Until resolved,
739 * do not try to negotiate SIREN usage when conferencing. PCMA or PCMU
740 * seems to work properly in this scenario.
742 while (i) {
743 PurpleMediaCodec *codec = i->data;
744 gchar *encoding_name = purple_media_codec_get_encoding_name(codec);
746 if (sipe_strequal(encoding_name,"THEORA") ||
747 (is_conference && sipe_strequal(encoding_name,"SIREN"))) {
748 GList *tmp;
749 g_object_unref(codec);
750 tmp = i->next;
751 codecs = g_list_delete_link(codecs, i);
752 i = tmp;
753 } else
754 i = i->next;
756 g_free(encoding_name);
759 return codecs;
762 struct sipe_backend_candidate *
763 sipe_backend_candidate_new(const gchar *foundation,
764 SipeComponentType component,
765 SipeCandidateType type, SipeNetworkProtocol proto,
766 const gchar *ip, guint port,
767 const gchar *username,
768 const gchar *password)
770 PurpleMediaCandidate *c = purple_media_candidate_new(
771 /* Libnice and Farsight rely on non-NULL foundation to
772 * distinguish between candidates of a component. When NULL
773 * foundation is passed (ie. ICE draft 6 does not use foudation),
774 * use username instead. If no foundation is provided, Farsight
775 * may signal an active candidate different from the one actually
776 * in use. See Farsight's agent_new_selected_pair() in
777 * fs-nice-stream-transmitter.h where first candidate in the
778 * remote list is always selected when no foundation. */
779 foundation ? foundation : username,
780 component,
781 sipe_candidate_type_to_purple(type),
782 sipe_network_protocol_to_purple(proto),
784 port);
785 g_object_set(c, "username", username, "password", password, NULL);
786 return (struct sipe_backend_candidate *)c;
789 void
790 sipe_backend_candidate_free(struct sipe_backend_candidate *candidate)
792 if (candidate)
793 g_object_unref(candidate);
796 gchar *
797 sipe_backend_candidate_get_username(struct sipe_backend_candidate *candidate)
799 /* Not explicitly documented, but return value must be g_free()'d */
800 return purple_media_candidate_get_username((PurpleMediaCandidate*)candidate);
803 gchar *
804 sipe_backend_candidate_get_password(struct sipe_backend_candidate *candidate)
806 /* Not explicitly documented, but return value must be g_free()'d */
807 return purple_media_candidate_get_password((PurpleMediaCandidate*)candidate);
810 gchar *
811 sipe_backend_candidate_get_foundation(struct sipe_backend_candidate *candidate)
813 /* Not explicitly documented, but return value must be g_free()'d */
814 return purple_media_candidate_get_foundation((PurpleMediaCandidate*)candidate);
817 gchar *
818 sipe_backend_candidate_get_ip(struct sipe_backend_candidate *candidate)
820 /* Not explicitly documented, but return value must be g_free()'d */
821 return purple_media_candidate_get_ip((PurpleMediaCandidate*)candidate);
824 guint
825 sipe_backend_candidate_get_port(struct sipe_backend_candidate *candidate)
827 return purple_media_candidate_get_port((PurpleMediaCandidate*)candidate);
830 gchar *
831 sipe_backend_candidate_get_base_ip(struct sipe_backend_candidate *candidate)
833 /* Not explicitly documented, but return value must be g_free()'d */
834 return purple_media_candidate_get_base_ip((PurpleMediaCandidate*)candidate);
837 guint
838 sipe_backend_candidate_get_base_port(struct sipe_backend_candidate *candidate)
840 return purple_media_candidate_get_base_port((PurpleMediaCandidate*)candidate);
843 guint32
844 sipe_backend_candidate_get_priority(struct sipe_backend_candidate *candidate)
846 return purple_media_candidate_get_priority((PurpleMediaCandidate*)candidate);
849 void
850 sipe_backend_candidate_set_priority(struct sipe_backend_candidate *candidate, guint32 priority)
852 g_object_set(candidate, "priority", priority, NULL);
855 SipeComponentType
856 sipe_backend_candidate_get_component_type(struct sipe_backend_candidate *candidate)
858 return purple_media_candidate_get_component_id((PurpleMediaCandidate*)candidate);
861 SipeCandidateType
862 sipe_backend_candidate_get_type(struct sipe_backend_candidate *candidate)
864 PurpleMediaCandidateType type =
865 purple_media_candidate_get_candidate_type((PurpleMediaCandidate*)candidate);
866 return purple_candidate_type_to_sipe(type);
869 SipeNetworkProtocol
870 sipe_backend_candidate_get_protocol(struct sipe_backend_candidate *candidate)
872 PurpleMediaNetworkProtocol proto =
873 purple_media_candidate_get_protocol((PurpleMediaCandidate*)candidate);
874 return purple_network_protocol_to_sipe(proto);
877 static void
878 remove_lone_candidate_cb(SIPE_UNUSED_PARAMETER gpointer key,
879 gpointer value,
880 gpointer user_data)
882 GList *entry = value;
883 GList **candidates = user_data;
885 g_object_unref(entry->data);
886 *candidates = g_list_delete_link(*candidates, entry);
889 static GList *
890 ensure_candidate_pairs(GList *candidates)
892 GHashTable *lone_cand_links;
893 GList *i;
895 lone_cand_links = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
897 for (i = candidates; i; i = i->next) {
898 PurpleMediaCandidate *c = i->data;
899 gchar *foundation = purple_media_candidate_get_foundation(c);
901 if (g_hash_table_lookup(lone_cand_links, foundation)) {
902 g_hash_table_remove(lone_cand_links, foundation);
903 g_free(foundation);
904 } else {
905 g_hash_table_insert(lone_cand_links, foundation, i);
909 g_hash_table_foreach(lone_cand_links, remove_lone_candidate_cb, &candidates);
910 g_hash_table_destroy(lone_cand_links);
912 return candidates;
915 GList *
916 sipe_backend_get_local_candidates(struct sipe_media_call *media,
917 struct sipe_media_stream *stream)
919 GList *candidates =
920 purple_media_get_local_candidates(media->backend_private->m,
921 stream->id,
922 media->with);
923 candidates = duplicate_tcp_candidates(candidates);
926 * Sometimes purple will not return complete list of candidates, even
927 * after "candidates-prepared" signal is emitted. This is a feature of
928 * libnice, namely affecting candidates discovered via UPnP. Nice does
929 * not wait until discovery is finished and can signal end of candidate
930 * gathering before all responses from UPnP enabled gateways are received.
932 * Remove any incomplete RTP+RTCP candidate pairs from the list.
934 candidates = ensure_candidate_pairs(candidates);
935 return candidates;
938 void
939 sipe_backend_media_accept(struct sipe_backend_media *media, gboolean local)
941 if (media)
942 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_ACCEPT,
943 NULL, NULL, local);
946 void
947 sipe_backend_media_hangup(struct sipe_backend_media *media, gboolean local)
949 if (media)
950 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_HANGUP,
951 NULL, NULL, local);
954 void
955 sipe_backend_media_reject(struct sipe_backend_media *media, gboolean local)
957 if (media)
958 purple_media_stream_info(media->m, PURPLE_MEDIA_INFO_REJECT,
959 NULL, NULL, local);
962 static PurpleMediaSessionType sipe_media_to_purple(SipeMediaType type)
964 switch (type) {
965 case SIPE_MEDIA_AUDIO: return PURPLE_MEDIA_AUDIO;
966 case SIPE_MEDIA_VIDEO: return PURPLE_MEDIA_VIDEO;
967 default: return PURPLE_MEDIA_NONE;
971 /*SipeMediaType purple_media_to_sipe(PurpleMediaSessionType type)
973 switch (type) {
974 case PURPLE_MEDIA_AUDIO: return SIPE_MEDIA_AUDIO;
975 case PURPLE_MEDIA_VIDEO: return SIPE_MEDIA_VIDEO;
976 default: return SIPE_MEDIA_AUDIO;
980 static PurpleMediaCandidateType
981 sipe_candidate_type_to_purple(SipeCandidateType type)
983 switch (type) {
984 case SIPE_CANDIDATE_TYPE_HOST: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
985 case SIPE_CANDIDATE_TYPE_RELAY: return PURPLE_MEDIA_CANDIDATE_TYPE_RELAY;
986 case SIPE_CANDIDATE_TYPE_SRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX;
987 case SIPE_CANDIDATE_TYPE_PRFLX: return PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX;
988 default: return PURPLE_MEDIA_CANDIDATE_TYPE_HOST;
992 static SipeCandidateType
993 purple_candidate_type_to_sipe(PurpleMediaCandidateType type)
995 switch (type) {
996 case PURPLE_MEDIA_CANDIDATE_TYPE_HOST: return SIPE_CANDIDATE_TYPE_HOST;
997 case PURPLE_MEDIA_CANDIDATE_TYPE_RELAY: return SIPE_CANDIDATE_TYPE_RELAY;
998 case PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX: return SIPE_CANDIDATE_TYPE_SRFLX;
999 case PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX: return SIPE_CANDIDATE_TYPE_PRFLX;
1000 default: return SIPE_CANDIDATE_TYPE_HOST;
1004 static PurpleMediaNetworkProtocol
1005 sipe_network_protocol_to_purple(SipeNetworkProtocol proto)
1007 switch (proto) {
1008 #ifdef HAVE_PURPLE_NEW_TCP_ENUMS
1009 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
1010 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE;
1011 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
1012 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE;
1013 case SIPE_NETWORK_PROTOCOL_TCP_SO:
1014 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_SO;
1015 #else
1016 case SIPE_NETWORK_PROTOCOL_TCP_ACTIVE:
1017 case SIPE_NETWORK_PROTOCOL_TCP_PASSIVE:
1018 case SIPE_NETWORK_PROTOCOL_TCP_SO:
1019 return PURPLE_MEDIA_NETWORK_PROTOCOL_TCP;
1020 #endif
1021 default:
1022 case SIPE_NETWORK_PROTOCOL_UDP:
1023 return PURPLE_MEDIA_NETWORK_PROTOCOL_UDP;
1027 static SipeNetworkProtocol
1028 purple_network_protocol_to_sipe(PurpleMediaNetworkProtocol proto)
1030 switch (proto) {
1031 #ifdef HAVE_PURPLE_NEW_TCP_ENUMS
1032 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_ACTIVE:
1033 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
1034 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_PASSIVE:
1035 return SIPE_NETWORK_PROTOCOL_TCP_PASSIVE;
1036 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP_SO:
1037 return SIPE_NETWORK_PROTOCOL_TCP_SO;
1038 #else
1039 case PURPLE_MEDIA_NETWORK_PROTOCOL_TCP:
1040 return SIPE_NETWORK_PROTOCOL_TCP_ACTIVE;
1041 #endif
1042 default:
1043 case PURPLE_MEDIA_NETWORK_PROTOCOL_UDP:
1044 return SIPE_NETWORK_PROTOCOL_UDP;
1048 #ifdef HAVE_SRTP
1049 SipeEncryptionPolicy
1050 sipe_backend_media_get_encryption_policy(struct sipe_core_public *sipe_public)
1052 PurpleAccount *account = sipe_public->backend_private->account;
1054 const char *policy =
1055 purple_account_get_string(account, "encryption-policy",
1056 "obey-server");
1058 if (sipe_strequal(policy, "disabled")) {
1059 return SIPE_ENCRYPTION_POLICY_REJECTED;
1060 } else if (sipe_strequal(policy, "optional")) {
1061 return SIPE_ENCRYPTION_POLICY_OPTIONAL;
1062 } else if (sipe_strequal(policy, "required")) {
1063 return SIPE_ENCRYPTION_POLICY_REQUIRED;
1064 } else {
1065 return SIPE_ENCRYPTION_POLICY_OBEY_SERVER;
1068 #else
1069 SipeEncryptionPolicy
1070 sipe_backend_media_get_encryption_policy(SIPE_UNUSED_PARAMETER struct sipe_core_public *sipe_public)
1072 return SIPE_ENCRYPTION_POLICY_REJECTED;
1074 #endif
1077 Local Variables:
1078 mode: c
1079 c-file-style: "bsd"
1080 indent-tabs-mode: t
1081 tab-width: 8
1082 End: