2 * This file is part of the Nice GLib ICE library.
4 * (C) 2006, 2007 Collabora Ltd.
5 * Contact: Dafydd Harries
6 * (C) 2006, 2007 Nokia Corporation. All rights reserved.
7 * Contact: Kai Vehmanen
9 * The contents of this file are subject to the Mozilla Public License Version
10 * 1.1 (the "License"); you may not use this file except in compliance with
11 * the License. You may obtain a copy of the License at
12 * http://www.mozilla.org/MPL/
14 * Software distributed under the License is distributed on an "AS IS" basis,
15 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
16 * for the specific language governing rights and limitations under the
19 * The Original Code is the Nice GLib ICE library.
21 * The Initial Developers of the Original Code are Collabora Ltd and Nokia
22 * Corporation. All Rights Reserved.
25 * Dafydd Harries, Collabora Ltd.
28 * Alternatively, the contents of this file may be used under the terms of the
29 * the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
30 * case the provisions of LGPL are applicable instead of those above. If you
31 * wish to allow use of your version of this file only under the terms of the
32 * LGPL and not to allow others to use your version of this file under the
33 * MPL, indicate your decision by deleting the provisions above and replace
34 * them with the notice and other provisions required by the LGPL. If you do
35 * not delete the provisions above, a recipient may use your version of this
36 * file under either the MPL or the LGPL.
41 * @brief ICE agent API implementation
51 #include <sys/select.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <arpa/inet.h>
58 #include "stun/usages/bind.h"
61 #include "candidate.h"
62 #include "component.h"
63 #include "conncheck.h"
64 #include "discovery.h"
66 #include "agent-priv.h"
67 #include "agent-signals-marshal.h"
71 /* This is the max size of a UDP packet
72 * will it work tcp relaying??
74 #define MAX_BUFFER_SIZE 65536
75 #define DEFAULT_STUN_PORT 3478
78 G_DEFINE_TYPE (NiceAgent
, nice_agent
, G_TYPE_OBJECT
);
82 PROP_SOCKET_FACTORY
= 1,
86 PROP_STUN_SERVER_PORT
,
88 PROP_TURN_SERVER_PORT
,
89 PROP_CONTROLLING_MODE
,
91 PROP_STUN_PACING_TIMER
,
92 PROP_MAX_CONNECTIVITY_CHECKS
98 SIGNAL_COMPONENT_STATE_CHANGED
,
99 SIGNAL_CANDIDATE_GATHERING_DONE
,
100 SIGNAL_NEW_SELECTED_PAIR
,
101 SIGNAL_NEW_CANDIDATE
,
102 SIGNAL_NEW_REMOTE_CANDIDATE
,
103 SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED
,
108 static guint signals
[N_SIGNALS
];
110 static gboolean
priv_attach_stream_component (NiceAgent
*agent
,
112 Component
*component
);
113 static void priv_detach_stream_component (Stream
*stream
, Component
*component
);
115 Stream
*agent_find_stream (NiceAgent
*agent
, guint stream_id
)
119 for (i
= agent
->streams
; i
; i
= i
->next
)
123 if (s
->id
== stream_id
)
132 agent_find_component (
137 Component
**component
)
142 s
= agent_find_stream (agent
, stream_id
);
147 c
= stream_find_component_by_id (s
, component_id
);
163 nice_agent_dispose (GObject
*object
);
166 nice_agent_get_property (
173 nice_agent_set_property (
181 nice_agent_class_init (NiceAgentClass
*klass
)
183 GObjectClass
*gobject_class
= G_OBJECT_CLASS (klass
);
185 gobject_class
->get_property
= nice_agent_get_property
;
186 gobject_class
->set_property
= nice_agent_set_property
;
187 gobject_class
->dispose
= nice_agent_dispose
;
189 /* install properties */
191 g_object_class_install_property (gobject_class
, PROP_SOCKET_FACTORY
,
192 g_param_spec_pointer (
194 "UDP socket factory",
195 "The socket factory used to create new UDP sockets",
196 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
199 g_object_class_install_property (gobject_class
, PROP_MAIN_CONTEXT
,
200 g_param_spec_pointer (
202 "The GMainContext to use for timeouts",
203 "The GMainContext to use for timeouts",
204 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
206 g_object_class_install_property (gobject_class
, PROP_COMPATIBILITY
,
209 "ICE specification compatibility",
210 "The compatibility mode for the agent",
211 NICE_COMPATIBILITY_ID19
, NICE_COMPATIBILITY_LAST
,
212 NICE_COMPATIBILITY_ID19
,
213 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
215 g_object_class_install_property (gobject_class
, PROP_STUN_SERVER
,
216 g_param_spec_string (
219 "The STUN server used to obtain server-reflexive candidates",
223 g_object_class_install_property (gobject_class
, PROP_STUN_SERVER_PORT
,
227 "The STUN server used to obtain server-reflexive candidates",
229 1, /* not a construct property, ignored */
232 g_object_class_install_property (gobject_class
, PROP_TURN_SERVER
,
233 g_param_spec_string (
236 "The TURN server used to obtain relay candidates",
240 g_object_class_install_property (gobject_class
, PROP_TURN_SERVER_PORT
,
244 "The TURN server used to obtain relay candidates",
246 1, /* not a construct property, ignored */
249 g_object_class_install_property (gobject_class
, PROP_CONTROLLING_MODE
,
250 g_param_spec_boolean (
252 "ICE controlling mode",
253 "Whether the agent is in controlling mode",
254 FALSE
, /* not a construct property, ignored */
257 g_object_class_install_property (gobject_class
, PROP_FULL_MODE
,
258 g_param_spec_boolean (
261 "Whether agent runs in ICE full mode",
262 TRUE
, /* use full mode by default */
263 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
265 g_object_class_install_property (gobject_class
, PROP_STUN_PACING_TIMER
,
269 "Timer 'Ta' (msecs) used in the IETF ICE specification for pacing candidate gathering and sending of connectivity checks",
271 NICE_AGENT_TIMER_TA_DEFAULT
,
272 G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
274 /* note: according to spec recommendation in sect 5.7.3 (ID-19) */
275 g_object_class_install_property (gobject_class
, PROP_MAX_CONNECTIVITY_CHECKS
,
277 "max-connectivity-checks",
278 "Maximum number of connectivity checks",
279 "Upper limit for the total number of connectivity checks performed",
281 0, /* default set in init */
284 /* install signals */
286 /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, guint state, gpointer self) */
287 signals
[SIGNAL_COMPONENT_STATE_CHANGED
] =
289 "component-state-changed",
290 G_OBJECT_CLASS_TYPE (klass
),
291 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
295 agent_marshal_VOID__UINT_UINT_UINT
,
298 G_TYPE_UINT
, G_TYPE_UINT
, G_TYPE_UINT
,
301 /* signature: void cb(NiceAgent *agent, gpointer self) */
302 signals
[SIGNAL_CANDIDATE_GATHERING_DONE
] =
304 "candidate-gathering-done",
305 G_OBJECT_CLASS_TYPE (klass
),
306 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
310 agent_marshal_VOID__VOID
,
315 /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id,
316 gchar *lfoundation, gchar* rfoundation, gpointer self) */
317 signals
[SIGNAL_NEW_SELECTED_PAIR
] =
320 G_OBJECT_CLASS_TYPE (klass
),
321 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
325 agent_marshal_VOID__UINT_UINT_STRING_STRING
,
328 G_TYPE_UINT
, G_TYPE_UINT
, G_TYPE_STRING
, G_TYPE_STRING
,
331 /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, gchar *foundation) */
332 signals
[SIGNAL_NEW_CANDIDATE
] =
335 G_OBJECT_CLASS_TYPE (klass
),
336 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
340 agent_marshal_VOID__UINT_UINT_STRING
,
343 G_TYPE_UINT
, G_TYPE_UINT
, G_TYPE_STRING
,
346 /* signature: void cb(NiceAgent *agent, guint stream_id, guint component_id, gchar *foundation) */
347 signals
[SIGNAL_NEW_REMOTE_CANDIDATE
] =
349 "new-remote-candidate",
350 G_OBJECT_CLASS_TYPE (klass
),
351 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
355 agent_marshal_VOID__UINT_UINT_STRING
,
358 G_TYPE_UINT
, G_TYPE_UINT
, G_TYPE_STRING
,
361 /* signature: void cb(NiceAgent *agent, guint stream_id, gpointer self) */
362 signals
[SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED
] =
364 "initial-binding-request-received",
365 G_OBJECT_CLASS_TYPE (klass
),
366 G_SIGNAL_RUN_LAST
| G_SIGNAL_DETAILED
,
370 agent_marshal_VOID__UINT
,
378 static void priv_generate_tie_breaker (NiceAgent
*agent
)
380 nice_rng_generate_bytes (agent
->rng
, 8, (gchar
*)&agent
->tie_breaker
);
384 nice_agent_init (NiceAgent
*agent
)
386 agent
->next_candidate_id
= 1;
387 agent
->next_stream_id
= 1;
389 /* set defaults; not construct params, so set here */
390 agent
->stun_server_port
= DEFAULT_STUN_PORT
;
391 agent
->turn_server_port
= DEFAULT_STUN_PORT
;
392 agent
->controlling_mode
= TRUE
;
393 agent
->max_conn_checks
= NICE_AGENT_MAX_CONNECTIVITY_CHECKS_DEFAULT
;
395 agent
->discovery_list
= NULL
;
396 agent
->discovery_unsched_items
= 0;
397 agent
->discovery_timer_id
= 0;
398 agent
->conncheck_timer_id
= 0;
399 agent
->keepalive_timer_id
= 0;
400 agent
->compatibility
= NICE_COMPATIBILITY_ID19
;
402 stun_agent_init (&agent
->stun_agent
, STUN_ALL_KNOWN_ATTRIBUTES
,
403 STUN_COMPATIBILITY_3489BIS
,
404 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS
|
405 STUN_AGENT_USAGE_USE_FINGERPRINT
);
407 agent
->rng
= nice_rng_new ();
408 priv_generate_tie_breaker (agent
);
410 g_static_rec_mutex_init (&agent
->mutex
);
416 * @factory: a NiceUDPSocketFactory used for allocating sockets
418 * Create a new NiceAgent.
420 * Returns: the new agent
422 NICEAPI_EXPORT NiceAgent
*
423 nice_agent_new (NiceUDPSocketFactory
*factory
,
424 GMainContext
*ctx
, NiceCompatibility compat
)
426 NiceAgent
*agent
= g_object_new (NICE_TYPE_AGENT
,
427 "socket-factory", factory
,
428 "compatibility", compat
,
437 nice_agent_get_property (
443 NiceAgent
*agent
= NICE_AGENT (object
);
445 g_static_rec_mutex_lock (&agent
->mutex
);
449 case PROP_SOCKET_FACTORY
:
450 g_value_set_pointer (value
, agent
->socket_factory
);
453 case PROP_MAIN_CONTEXT
:
454 g_value_set_pointer (value
, agent
->main_context
);
457 case PROP_COMPATIBILITY
:
458 g_value_set_uint (value
, agent
->compatibility
);
461 case PROP_STUN_SERVER
:
462 g_value_set_string (value
, agent
->stun_server_ip
);
465 case PROP_STUN_SERVER_PORT
:
466 g_value_set_uint (value
, agent
->stun_server_port
);
469 case PROP_TURN_SERVER
:
470 g_value_set_string (value
, agent
->turn_server_ip
);
473 case PROP_TURN_SERVER_PORT
:
474 g_value_set_uint (value
, agent
->turn_server_port
);
477 case PROP_CONTROLLING_MODE
:
478 g_value_set_boolean (value
, agent
->controlling_mode
);
482 g_value_set_boolean (value
, agent
->full_mode
);
485 case PROP_STUN_PACING_TIMER
:
486 g_value_set_uint (value
, agent
->timer_ta
);
489 case PROP_MAX_CONNECTIVITY_CHECKS
:
490 g_value_set_uint (value
, agent
->max_conn_checks
);
491 /* XXX: should we prune the list of already existing checks? */
495 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
498 g_static_rec_mutex_unlock (&agent
->mutex
);
503 nice_agent_set_property (
509 NiceAgent
*agent
= NICE_AGENT (object
);
511 g_static_rec_mutex_lock (&agent
->mutex
);
515 case PROP_SOCKET_FACTORY
:
516 agent
->socket_factory
= g_value_get_pointer (value
);
519 case PROP_MAIN_CONTEXT
:
520 agent
->main_context
= g_value_get_pointer (value
);
523 case PROP_COMPATIBILITY
:
524 agent
->compatibility
= g_value_get_uint (value
);
525 if (agent
->compatibility
== NICE_COMPATIBILITY_ID19
) {
526 stun_agent_init (&agent
->stun_agent
, STUN_ALL_KNOWN_ATTRIBUTES
,
527 STUN_COMPATIBILITY_3489BIS
,
528 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS
|
529 STUN_AGENT_USAGE_USE_FINGERPRINT
);
530 } else if (agent
->compatibility
== NICE_COMPATIBILITY_GOOGLE
) {
531 stun_agent_init (&agent
->stun_agent
, STUN_ALL_KNOWN_ATTRIBUTES
,
532 STUN_COMPATIBILITY_RFC3489
,
533 STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS
|
534 STUN_AGENT_USAGE_IGNORE_CREDENTIALS
);
539 case PROP_STUN_SERVER
:
540 agent
->stun_server_ip
= g_value_dup_string (value
);
543 case PROP_STUN_SERVER_PORT
:
544 agent
->stun_server_port
= g_value_get_uint (value
);
547 case PROP_TURN_SERVER
:
548 agent
->turn_server_ip
= g_value_dup_string (value
);
551 case PROP_CONTROLLING_MODE
:
552 agent
->controlling_mode
= g_value_get_boolean (value
);
556 agent
->full_mode
= g_value_get_boolean (value
);
559 case PROP_STUN_PACING_TIMER
:
560 agent
->timer_ta
= g_value_get_uint (value
);
563 case PROP_MAX_CONNECTIVITY_CHECKS
:
564 agent
->max_conn_checks
= g_value_get_uint (value
);
568 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
571 g_static_rec_mutex_unlock (&agent
->mutex
);
575 void agent_gathering_done (NiceAgent
*agent
)
578 GSList
*i
, *j
, *k
, *l
, *m
;
580 for (i
= agent
->streams
; i
; i
= i
->next
) {
581 Stream
*stream
= i
->data
;
582 for (j
= stream
->components
; j
; j
= j
->next
) {
583 Component
*component
= j
->data
;
585 for (k
= component
->local_candidates
; k
; k
= k
->next
) {
586 NiceCandidate
*local_candidate
= k
->data
;
588 for (l
= component
->remote_candidates
; l
; l
= l
->next
) {
589 NiceCandidate
*remote_candidate
= l
->data
;
591 for (m
= stream
->conncheck_list
; m
; m
= m
->next
) {
592 CandidateCheckPair
*p
= m
->data
;
594 if (p
->local
== local_candidate
&& p
->remote
== remote_candidate
)
598 conn_check_add_for_candidate (agent
, stream
->id
, component
, remote_candidate
);
605 agent_signal_gathering_done (agent
);
608 void agent_signal_gathering_done (NiceAgent
*agent
)
610 g_signal_emit (agent
, signals
[SIGNAL_CANDIDATE_GATHERING_DONE
], 0);
613 void agent_signal_initial_binding_request_received (NiceAgent
*agent
, Stream
*stream
)
615 if (stream
->initial_binding_request_received
!= TRUE
) {
616 stream
->initial_binding_request_received
= TRUE
;
617 g_signal_emit (agent
, signals
[SIGNAL_INITIAL_BINDING_REQUEST_RECEIVED
], 0, stream
->id
);
621 void agent_signal_new_selected_pair (NiceAgent
*agent
, guint stream_id
, guint component_id
, const gchar
*local_foundation
, const gchar
*remote_foundation
)
623 Component
*component
;
627 if (!agent_find_component (agent
, stream_id
, component_id
, NULL
, &component
))
630 lf_copy
= g_strdup (local_foundation
);
631 rf_copy
= g_strdup (remote_foundation
);
634 g_signal_emit (agent
, signals
[SIGNAL_NEW_SELECTED_PAIR
], 0,
635 stream_id
, component_id
, lf_copy
, rf_copy
);
641 void agent_signal_new_candidate (NiceAgent
*agent
, NiceCandidate
*candidate
)
643 g_signal_emit (agent
, signals
[SIGNAL_NEW_CANDIDATE
], 0,
644 candidate
->stream_id
,
645 candidate
->component_id
,
646 candidate
->foundation
);
649 void agent_signal_new_remote_candidate (NiceAgent
*agent
, NiceCandidate
*candidate
)
651 g_signal_emit (agent
, signals
[SIGNAL_NEW_REMOTE_CANDIDATE
], 0,
652 candidate
->stream_id
,
653 candidate
->component_id
,
654 candidate
->foundation
);
657 void agent_signal_component_state_change (NiceAgent
*agent
, guint stream_id
, guint component_id
, NiceComponentState state
)
659 Component
*component
;
661 if (!agent_find_component (agent
, stream_id
, component_id
, NULL
, &component
))
664 if (component
->state
!= state
&& state
< NICE_COMPONENT_STATE_LAST
) {
665 g_debug ("Agent %p : stream %u component %u STATE-CHANGE %u -> %u.", agent
,
666 stream_id
, component_id
, component
->state
, state
);
668 component
->state
= state
;
670 g_signal_emit (agent
, signals
[SIGNAL_COMPONENT_STATE_CHANGED
], 0,
671 stream_id
, component_id
, state
);
676 agent_candidate_pair_priority (NiceAgent
*agent
, NiceCandidate
*local
, NiceCandidate
*remote
)
678 if (agent
->controlling_mode
== TRUE
)
679 return nice_candidate_pair_priority (local
->priority
, remote
->priority
);
681 return nice_candidate_pair_priority (remote
->priority
, local
->priority
);
685 priv_add_srv_rfx_candidate_discovery (NiceAgent
*agent
, NiceCandidate
*host_candidate
, const gchar
*stun_server_ip
, const guint stun_server_port
, Stream
*stream
, guint component_id
, NiceAddress
*addr
)
687 CandidateDiscovery
*cdisco
;
688 GSList
*modified_list
;
690 /* note: no need to check for redundant candidates, as this is
691 * done later on in the process */
693 cdisco
= g_slice_new0 (CandidateDiscovery
);
695 modified_list
= g_slist_append (agent
->discovery_list
, cdisco
);
698 cdisco
->type
= NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE
;
699 cdisco
->socket
= host_candidate
->sockptr
->fileno
;
700 cdisco
->nicesock
= host_candidate
->sockptr
;
701 cdisco
->server_addr
= stun_server_ip
;
702 cdisco
->server_port
= stun_server_port
;
703 cdisco
->interface
= addr
;
704 cdisco
->stream
= stream
;
705 cdisco
->component
= stream_find_component_by_id (stream
, component_id
);
706 cdisco
->agent
= agent
;
707 g_debug ("Agent %p : Adding new srv-rflx candidate discovery %p\n", agent
, cdisco
);
708 agent
->discovery_list
= modified_list
;
709 ++agent
->discovery_unsched_items
;
719 * nice_agent_add_stream:
720 * @agent: a NiceAgent
721 * @n_components: number of components
723 * Add a data stream to @agent.
725 * @pre local addresses must be set with nice_agent_add_local_address()
727 * Returns: the ID of the new stream, 0 on failure
730 nice_agent_add_stream (
735 GSList
*modified_list
= NULL
;
738 g_static_rec_mutex_lock (&agent
->mutex
);
740 if (!agent
->local_addresses
) {
744 stream
= stream_new (n_components
);
746 modified_list
= g_slist_append (agent
->streams
, stream
);
748 stream
->id
= agent
->next_stream_id
++;
749 g_debug ("Agent %p : allocating stream id %u (%p)", agent
, stream
->id
, stream
);
751 stream_initialize_credentials (stream
, agent
->rng
);
753 agent
->streams
= modified_list
;
756 stream_free (stream
);
762 g_static_rec_mutex_unlock (&agent
->mutex
);
768 * nice_agent_gather_candidates:
770 * start the candidate gathering process
774 nice_agent_gather_candidates (
782 g_static_rec_mutex_lock (&agent
->mutex
);
784 stream
= agent_find_stream (agent
, stream_id
);
785 if (stream
== NULL
) {
789 g_debug ("Agent %p : In %s mode, starting candidate gathering.", agent
, agent
->full_mode
? "ICE-FULL" : "ICE-LITE");
791 /* generate a local host candidate for each local address */
793 for (i
= agent
->local_addresses
; i
; i
= i
->next
)
795 NiceAddress
*addr
= i
->data
;
796 NiceCandidate
*host_candidate
;
798 for (n
= 0; n
< stream
->n_components
; n
++) {
799 host_candidate
= discovery_add_local_host_candidate (agent
, stream
->id
,
802 if (!host_candidate
) {
803 g_error ("No host candidate??");
807 if (agent
->full_mode
&&
808 agent
->stun_server_ip
) {
811 priv_add_srv_rfx_candidate_discovery (agent
,
813 agent
->stun_server_ip
,
814 agent
->stun_server_port
,
816 n
+ 1 /* component-id */,
820 /* note: memory allocation failure, return error */
821 g_error ("Memory allocation failure?");
828 /* note: no async discoveries pending, signal that we are ready */
829 if (agent
->discovery_unsched_items
== 0) {
830 agent_gathering_done (agent
);
832 g_assert (agent
->discovery_list
);
833 discovery_schedule (agent
);
838 g_static_rec_mutex_unlock (&agent
->mutex
);
841 static void priv_remove_keepalive_timer (NiceAgent
*agent
)
843 if (agent
->keepalive_timer_id
) {
844 g_source_remove (agent
->keepalive_timer_id
),
845 agent
->keepalive_timer_id
= 0;
850 * nice_agent_remove_stream:
851 * @agent: a NiceAgent
852 * @stream_id: the ID of the stream to remove
855 nice_agent_remove_stream (
859 /* note that streams/candidates can be in use by other threads */
864 g_static_rec_mutex_lock (&agent
->mutex
);
865 stream
= agent_find_stream (agent
, stream_id
);
871 /* note: remove items with matching stream_ids from both lists */
872 conn_check_prune_stream (agent
, stream
);
873 discovery_prune_stream (agent
, stream_id
);
875 /* remove the stream itself */
876 for (i
= stream
->components
; i
; i
= i
->next
) {
877 priv_detach_stream_component (stream
, (Component
*) i
->data
);
880 agent
->streams
= g_slist_remove (agent
->streams
, stream
);
881 stream_free (stream
);
884 priv_remove_keepalive_timer (agent
);
887 g_static_rec_mutex_unlock (&agent
->mutex
);
891 * nice_agent_add_local_address:
892 * @agent: A NiceAgent
893 * @addr: the address of a local IP interface
895 * Inform the agent of the presence of an address that a local
896 * network interface is bound to.
898 * @return FALSE on fatal (memory allocation) errors
900 NICEAPI_EXPORT gboolean
901 nice_agent_add_local_address (NiceAgent
*agent
, NiceAddress
*addr
)
904 GSList
*modified_list
;
905 gboolean ret
= FALSE
;
907 g_static_rec_mutex_lock (&agent
->mutex
);
909 dup
= nice_address_dup (addr
);
910 nice_address_set_port (dup
, 0);
911 modified_list
= g_slist_append (agent
->local_addresses
, dup
);
913 agent
->local_addresses
= modified_list
;
920 g_static_rec_mutex_unlock (&agent
->mutex
);
925 * Adds a new, or updates an existing, remote candidate.
927 * @return TRUE if candidate was succesfully added or
928 * update, otherwise FALSE
930 static gboolean
priv_add_remote_candidate (
934 NiceCandidateType type
,
935 const NiceAddress
*addr
,
936 const NiceAddress
*base_addr
,
937 NiceCandidateTransport transport
,
939 const gchar
*username
,
940 const gchar
*password
,
941 const gchar
*foundation
)
943 Component
*component
;
944 NiceCandidate
*candidate
;
945 gchar
*username_dup
= NULL
, *password_dup
= NULL
;
946 gboolean error_flag
= FALSE
;
948 if (!agent_find_component (agent
, stream_id
, component_id
, NULL
, &component
))
951 /* step: check whether the candidate already exists */
952 candidate
= component_find_remote_candidate(component
, addr
, transport
);
954 g_debug ("Agent %p : Update existing remote candidate %p.", agent
, candidate
);
955 /* case 1: an existing candidate, update the attributes */
956 candidate
->type
= type
;
958 candidate
->base_addr
= *base_addr
;
959 candidate
->priority
= priority
;
961 strncpy(candidate
->foundation
, foundation
, NICE_CANDIDATE_MAX_FOUNDATION
);
962 /* note: username and password must remain the same during
963 * a session; see sect 9.1.2 in ICE ID-19 */
964 if (conn_check_add_for_candidate (agent
, stream_id
, component
, candidate
) < 0)
968 /* case 2: add a new candidate */
970 username_dup
= g_strdup (username
);
972 password_dup
= g_strdup (password
);
974 candidate
= nice_candidate_new (type
);
976 GSList
*modified_list
= g_slist_append (component
->remote_candidates
, candidate
);
978 component
->remote_candidates
= modified_list
;
980 candidate
->stream_id
= stream_id
;
981 candidate
->component_id
= component_id
;
983 candidate
->type
= type
;
985 candidate
->addr
= *addr
;
988 gchar tmpbuf
[INET6_ADDRSTRLEN
];
989 nice_address_to_string (addr
, tmpbuf
);
990 g_debug ("Agent %p : Adding remote candidate with addr [%s]:%u.", agent
, tmpbuf
,
991 nice_address_get_port (addr
));
996 candidate
->base_addr
= *base_addr
;
998 candidate
->transport
= transport
;
999 candidate
->priority
= priority
;
1000 candidate
->username
= username_dup
;
1001 candidate
->password
= password_dup
;
1004 g_strlcpy (candidate
->foundation
, foundation
, NICE_CANDIDATE_MAX_FOUNDATION
);
1006 if (conn_check_add_for_candidate (agent
, stream_id
, component
, candidate
) < 0)
1009 else /* memory alloc error: list insert */
1012 else /* memory alloc error: candidate creation */
1018 nice_candidate_free (candidate
);
1019 g_free (username_dup
);
1020 g_free (password_dup
);
1028 * Sets the remote credentials for stream 'stream_id'.
1030 * Note: stream credentials do not override per-candidate
1031 * credentials if set
1033 * @agent: a NiceAgent
1034 * @stream_id: identifier returnedby nice_agent_add_stream()
1035 * @ufrag: NULL-terminated string containing an ICE username fragment
1036 * @pwd: NULL-terminated string containing an ICE password
1038 * @return TRUE on success
1040 NICEAPI_EXPORT gboolean
1041 nice_agent_set_remote_credentials (
1044 const gchar
*ufrag
, const gchar
*pwd
)
1047 gboolean ret
= FALSE
;
1049 g_static_rec_mutex_lock (&agent
->mutex
);
1051 stream
= agent_find_stream (agent
, stream_id
);
1052 /* note: oddly enough, ufrag and pwd can be empty strings */
1053 if (stream
&& ufrag
&& pwd
) {
1055 g_strlcpy (stream
->remote_ufrag
, ufrag
, NICE_STREAM_MAX_UFRAG
);
1056 g_strlcpy (stream
->remote_password
, pwd
, NICE_STREAM_MAX_PWD
);
1063 g_static_rec_mutex_unlock (&agent
->mutex
);
1068 * Gets the local credentials for stream 'stream_id'.
1070 * @agent: a NiceAgent
1071 * @stream_id: identifier returnedby nice_agent_add_stream()
1072 * @ufrag: a pointer to a NULL-terminated string containing
1073 * an ICE username fragment [OUT]
1074 * @pwd: a pointer to a NULL-terminated string containing an ICE
1077 * @return TRUE on success
1079 NICEAPI_EXPORT gboolean
1080 nice_agent_get_local_credentials (
1083 const gchar
**ufrag
, const gchar
**pwd
)
1086 gboolean ret
= TRUE
;
1088 g_static_rec_mutex_lock (&agent
->mutex
);
1090 stream
= agent_find_stream (agent
, stream_id
);
1091 if (stream
== NULL
) {
1095 if (!ufrag
|| !pwd
) {
1099 *ufrag
= stream
->local_ufrag
;
1100 *pwd
= stream
->local_password
;
1105 g_static_rec_mutex_unlock (&agent
->mutex
);
1110 * nice_agent_add_remote_candidate
1111 * @agent: a NiceAgent
1112 * @stream_id: the ID of the stream the candidate is for
1113 * @component_id: the ID of the component the candidate is for
1114 * @type: the type of the new candidate
1115 * @addr: the new candidate's IP address
1116 * @username: the new candidate's username (XXX: candidates don't have usernames)
1117 * @password: the new candidate's password (XXX: candidates don't have usernames)
1119 * Add a candidate our peer has informed us about to the agent's list.
1121 * Note: NICE_AGENT_MAX_REMOTE_CANDIDATES is the absolute
1122 * maximum limit for remote candidates
1123 * @return FALSE on fatal (memory alloc) errors
1125 NICEAPI_EXPORT gboolean
1126 nice_agent_add_remote_candidate (
1130 NiceCandidateType type
,
1132 const gchar
*username
,
1133 const gchar
*password
)
1136 /* XXX: to be deprecated */
1138 g_static_rec_mutex_lock (&agent
->mutex
);
1140 /* XXX: should we allow use of this method without an
1141 * initial call to nice_agent_set_remote_candidates()
1142 * with an empty set? */
1145 priv_add_remote_candidate (agent
,
1151 NICE_CANDIDATE_TRANSPORT_UDP
,
1157 /* XXX/later: for each component, generate a new check with the new
1158 candidate, see below set_remote_candidates() */
1161 g_static_rec_mutex_unlock (&agent
->mutex
);
1166 * nice_agent_set_remote_candidates
1167 * @agent: a NiceAgent
1168 * @stream_id: the ID of the stream the candidate is for
1169 * @component_id: the ID of the component the candidate is for
1170 * @candidates: a list of NiceCandidate items describing the candidates
1172 * Sets the remote candidates for a component of a stream. Replaces
1173 * any existing remote candidates.
1175 * Note: NICE_AGENT_MAX_REMOTE_CANDIDATES is the absolute
1176 * maximum limit for remote candidates
1178 * @return number of candidates added, negative on fatal (memory
1182 nice_agent_set_remote_candidates (NiceAgent
*agent
, guint stream_id
, guint component_id
, const GSList
*candidates
)
1188 if (agent
->discovery_unsched_items
> 0)
1191 g_static_rec_mutex_lock (&agent
->mutex
);
1193 for (i
= candidates
; i
&& added
>= 0; i
= i
->next
) {
1194 NiceCandidate
*d
= (NiceCandidate
*) i
->data
;
1196 priv_add_remote_candidate (agent
,
1213 conn_check_remote_candidates_set(agent
);
1216 gboolean res
= conn_check_schedule_next (agent
);
1218 g_debug ("Agent %p : Warning: unable to schedule any conn checks!", agent
);
1221 g_static_rec_mutex_unlock (&agent
->mutex
);
1227 * Reads data from a ready, nonblocking socket attached to an ICE
1230 * @return number of octets received, or negative on error
1236 Component
*component
,
1237 NiceUDPSocket
*udp_socket
,
1244 len
= nice_udp_socket_recv (udp_socket
, &from
,
1249 gchar tmpbuf
[INET6_ADDRSTRLEN
];
1250 nice_address_to_string (&from
, tmpbuf
);
1251 g_debug ("Agent %p : Packet received on local socket %u from [%s]:%u (%u octets).", agent
,
1252 udp_socket
->fileno
, tmpbuf
, nice_address_get_port (&from
), len
);
1259 if ((guint
)len
> buf_len
)
1261 /* buffer is not big enough to accept this packet */
1262 /* XXX: test this case */
1266 if (stun_message_validate_buffer_length ((uint8_t *) buf
, (size_t) len
) == len
) {
1267 /* If the retval is no 0, its not a valid stun packet, probably data */
1268 if (conn_check_handle_inbound_stun (agent
, stream
, component
, udp_socket
,
1269 &from
, buf
, len
) == FALSE
) {
1270 /* unhandled STUN, pass to client */
1274 /* not STUN, pass to client */
1278 /* handled STUN message*/
1284 * @agent: a NiceAgent
1285 * @stream_id: the ID of the stream to recieve data from
1286 * @component_id: the ID of the component to receive data from
1287 * @buf_len: the size of @buf
1288 * @buf: the buffer to read data into
1290 * Receive data on a particular component.
1292 * Returns: the amount of data read into @buf
1294 NICEAPI_EXPORT guint
1308 Component
*component
;
1311 g_static_rec_mutex_lock (&agent
->mutex
);
1312 if (!agent_find_component (agent
, stream_id
, component_id
, &stream
, &component
)) {
1318 for (i
= component
->sockets
; i
; i
= i
->next
)
1320 NiceUDPSocket
*sockptr
= i
->data
;
1322 FD_SET (sockptr
->fileno
, &fds
);
1323 max_fd
= MAX (sockptr
->fileno
, max_fd
);
1326 /* Loop on candidate sockets until we find one that has non-STUN data
1332 num_readable
= select (max_fd
+ 1, &fds
, NULL
, NULL
, NULL
);
1333 g_assert (num_readable
>= 0);
1335 if (num_readable
> 0)
1339 for (j
= 0; j
<= max_fd
; j
++)
1340 if (FD_ISSET (j
, &fds
))
1342 NiceUDPSocket
*socket
;
1344 socket
= component_find_udp_socket_by_fd (component
, j
);
1347 len
= _nice_agent_recv (agent
, stream
, component
, socket
,
1358 /* note: commented out to avoid compiler warnings
1360 * g_assert_not_reached (); */
1362 g_static_rec_mutex_unlock (&agent
->mutex
);
1366 NICEAPI_EXPORT guint
1367 nice_agent_recv_sock (
1375 NiceUDPSocket
*socket
;
1377 Component
*component
;
1380 g_static_rec_mutex_lock (&agent
->mutex
);
1381 if (!agent_find_component (agent
, stream_id
, component_id
, &stream
, &component
)) {
1385 socket
= component_find_udp_socket_by_fd (component
, sock
);
1388 ret
= _nice_agent_recv (agent
, stream
, component
,
1389 socket
, buf_len
, buf
);
1392 g_static_rec_mutex_unlock (&agent
->mutex
);
1398 * nice_agent_poll_read:
1399 * @agent: A NiceAgent
1400 * @other_fds: A GSList of other file descriptors to poll
1402 * Polls the agent's sockets until at least one of them is readable, and
1403 * additionally if @other_fds is not NULL, polls those for readability too.
1404 * @other_fds should contain the file descriptors directly, i.e. using
1407 * Returns: A list of file descriptors from @other_fds that are readable
1409 NICEAPI_EXPORT GSList
*
1410 nice_agent_poll_read (
1413 NiceAgentRecvFunc func
,
1423 g_static_rec_mutex_lock (&agent
->mutex
);
1427 for (i
= agent
->streams
; i
; i
= i
->next
)
1430 Stream
*stream
= i
->data
;
1432 for (k
= stream
->components
; k
; k
= k
->next
)
1434 Component
*component
= k
->data
;
1436 for (j
= component
->sockets
; j
; j
= j
->next
)
1438 NiceUDPSocket
*sockptr
= j
->data
;
1440 FD_SET (sockptr
->fileno
, &fds
);
1441 max_fd
= MAX (sockptr
->fileno
, max_fd
);
1446 for (i
= other_fds
; i
; i
= i
->next
)
1450 fileno
= GPOINTER_TO_UINT (i
->data
);
1451 FD_SET (fileno
, &fds
);
1452 max_fd
= MAX (fileno
, max_fd
);
1455 num_readable
= select (max_fd
+ 1, &fds
, NULL
, NULL
, NULL
);
1457 if (num_readable
< 1) {
1458 /* none readable, or error */
1462 for (j
= 0; j
<= max_fd
; j
++)
1463 if (FD_ISSET (j
, &fds
))
1465 if (g_slist_find (other_fds
, GUINT_TO_POINTER (j
))) {
1466 GSList
*modified_list
= g_slist_append (ret
, GUINT_TO_POINTER (j
));
1467 if (modified_list
== NULL
) {
1471 ret
= modified_list
;
1475 NiceUDPSocket
*socket
= NULL
;
1476 Stream
*stream
= NULL
;
1477 Component
*component
= NULL
;
1478 gchar buf
[MAX_BUFFER_SIZE
];
1481 for (i
= agent
->streams
; i
; i
= i
->next
)
1483 Stream
*s
= i
->data
;
1484 Component
*c
= stream_find_component_by_fd (s
, j
);
1486 socket
= component_find_udp_socket_by_fd (c
, j
);
1488 if (socket
!= NULL
) {
1495 if (socket
== NULL
|| stream
== NULL
|| component
== NULL
)
1498 len
= _nice_agent_recv (agent
, stream
, component
,
1499 socket
, MAX_BUFFER_SIZE
, buf
);
1501 if (len
&& func
!= NULL
)
1502 func (agent
, stream
->id
, component
->id
, len
, buf
,
1508 g_static_rec_mutex_unlock (&agent
->mutex
);
1515 * Sends a data payload over a stream component.
1517 * @pre component state MUST be NICE_COMPONENT_STATE_READY,
1518 * or as a special case, in any state if component was
1519 * in READY state before and was then restarted
1521 * @return number of bytes sent, or negative error code
1532 Component
*component
;
1535 g_static_rec_mutex_lock (&agent
->mutex
);
1537 if (!agent_find_component (agent
, stream_id
, component_id
, &stream
, &component
)) {
1541 if (component
->selected_pair
.local
!= NULL
)
1543 NiceUDPSocket
*sock
;
1547 gchar tmpbuf
[INET6_ADDRSTRLEN
];
1548 nice_address_to_string (&component
->selected_pair
.remote
->addr
, tmpbuf
);
1550 g_debug ("Agent %p : s%d:%d: sending %d bytes to [%s]:%d", agent
, stream_id
, component_id
,
1552 nice_address_get_port (&component
->selected_pair
.remote
->addr
));
1555 sock
= component
->selected_pair
.local
->sockptr
;
1556 addr
= &component
->selected_pair
.remote
->addr
;
1557 nice_udp_socket_send (sock
, addr
, len
, buf
);
1558 component
->media_after_tick
= TRUE
;
1565 g_static_rec_mutex_unlock (&agent
->mutex
);
1571 * nice_agent_get_local_candidates:
1572 * @agent: A NiceAgent
1574 * The caller owns the returned GSList but not the candidates contained within
1575 * it. To get full results, the client should wait for the
1576 * 'candidates-gathering-done' signal.
1578 * Returns: a GSList of local candidates (NiceCandidate) belonging to @agent
1580 NICEAPI_EXPORT GSList
*
1581 nice_agent_get_local_candidates (
1586 Component
*component
;
1587 GSList
* ret
= NULL
;
1588 GSList
* item
= NULL
;
1590 g_static_rec_mutex_lock (&agent
->mutex
);
1591 if (!agent_find_component (agent
, stream_id
, component_id
, NULL
, &component
))
1596 for (item
= component
->local_candidates
; item
; item
= item
->next
)
1597 ret
= g_slist_append (ret
, nice_candidate_copy (item
->data
));
1600 g_static_rec_mutex_unlock (&agent
->mutex
);
1606 * nice_agent_get_remote_candidates:
1607 * @agent: A NiceAgent
1609 * The caller owns the returned GSList but not the candidates contained within
1612 * Note: the list of remote candidates can change during processing.
1613 * The client should register for the "new-remote-candidate" signal to
1614 * get notification of new remote candidates.
1616 * Returns: a GSList of remote candidates (NiceCandidate) belonging to @agent
1618 NICEAPI_EXPORT GSList
*
1619 nice_agent_get_remote_candidates (
1624 Component
*component
;
1625 GSList
*ret
= NULL
, *item
= NULL
;
1627 g_static_rec_mutex_lock (&agent
->mutex
);
1628 if (!agent_find_component (agent
, stream_id
, component_id
, NULL
, &component
))
1633 for (item
= component
->remote_candidates
; item
; item
= item
->next
)
1634 ret
= g_slist_append (ret
, nice_candidate_copy (item
->data
));
1637 g_static_rec_mutex_unlock (&agent
->mutex
);
1642 * nice_agent_restart
1643 * @agent: A NiceAgent
1645 * Restarts the session as defined in ICE spec (ID-19). This function
1646 * needs to be called both when initiating (ICE spec section 9.1.1.1.
1647 * "ICE Restarts"), as well as when reacting (spec section 9.2.1.1.
1648 * "Detecting ICE Restart") to a restart.
1650 * Returns: FALSE on error
1653 nice_agent_restart (
1657 gboolean res
= TRUE
;
1659 g_static_rec_mutex_lock (&agent
->mutex
);
1661 /* step: clean up all connectivity checks */
1662 conn_check_free (agent
);
1664 /* step: regenerate tie-breaker value */
1665 priv_generate_tie_breaker (agent
);
1667 for (i
= agent
->streams
; i
&& res
; i
= i
->next
) {
1668 Stream
*stream
= i
->data
;
1670 /* step: reset local credentials for the stream and
1671 * clean up the list of remote candidates */
1672 res
= stream_restart (stream
, agent
->rng
);
1675 g_static_rec_mutex_unlock (&agent
->mutex
);
1680 nice_agent_dispose (GObject
*object
)
1683 NiceAgent
*agent
= NICE_AGENT (object
);
1685 /* step: free resources for the binding discovery timers */
1686 discovery_free (agent
);
1687 g_assert (agent
->discovery_list
== NULL
);
1689 /* step: free resources for the connectivity check timers */
1690 conn_check_free (agent
);
1692 priv_remove_keepalive_timer (agent
);
1694 for (i
= agent
->local_addresses
; i
; i
= i
->next
)
1696 NiceAddress
*a
= i
->data
;
1698 nice_address_free (a
);
1701 g_slist_free (agent
->local_addresses
);
1702 agent
->local_addresses
= NULL
;
1704 for (i
= agent
->streams
; i
; i
= i
->next
)
1706 Stream
*s
= i
->data
;
1711 g_slist_free (agent
->streams
);
1712 agent
->streams
= NULL
;
1714 g_free (agent
->stun_server_ip
);
1715 agent
->stun_server_ip
= NULL
;
1716 g_free (agent
->turn_server_ip
);
1717 agent
->turn_server_ip
= NULL
;
1719 nice_rng_free (agent
->rng
);
1722 if (G_OBJECT_CLASS (nice_agent_parent_class
)->dispose
)
1723 G_OBJECT_CLASS (nice_agent_parent_class
)->dispose (object
);
1725 g_static_rec_mutex_free (&agent
->mutex
);
1729 typedef struct _IOCtx IOCtx
;
1735 Component
*component
;
1736 NiceUDPSocket
*socket
;
1744 Component
*component
,
1745 NiceUDPSocket
*socket
)
1749 ctx
= g_slice_new0 (IOCtx
);
1752 ctx
->stream
= stream
;
1753 ctx
->component
= component
;
1754 ctx
->socket
= socket
;
1761 io_ctx_free (IOCtx
*ctx
)
1763 g_slice_free (IOCtx
, ctx
);
1767 nice_agent_g_source_cb (
1770 GIOCondition condition
,
1773 /* return value is whether to keep the source */
1776 NiceAgent
*agent
= ctx
->agent
;
1777 Stream
*stream
= ctx
->stream
;
1778 Component
*component
= ctx
->component
;
1779 gchar buf
[MAX_BUFFER_SIZE
];
1782 g_static_rec_mutex_lock (&agent
->mutex
);
1784 /* note: dear compiler, these are for you: */
1787 len
= _nice_agent_recv (agent
, stream
, component
, ctx
->socket
,
1788 MAX_BUFFER_SIZE
, buf
);
1790 if (len
> 0 && component
->g_source_io_cb
)
1791 component
->g_source_io_cb (agent
, stream
->id
, component
->id
,
1792 len
, buf
, component
->data
);
1794 g_static_rec_mutex_unlock (&agent
->mutex
);
1799 * Attaches one socket handle to the main loop event context
1803 priv_attach_stream_component_socket (NiceAgent
*agent
,
1805 Component
*component
,
1806 NiceUDPSocket
*udp_socket
)
1812 if (!component
->ctx
)
1815 io
= g_io_channel_unix_new (udp_socket
->fileno
);
1816 /* note: without G_IO_ERR the glib mainloop goes into
1817 * busyloop if errors are encountered */
1818 source
= g_io_create_watch (io
, G_IO_IN
| G_IO_ERR
);
1819 ctx
= io_ctx_new (agent
, stream
, component
, udp_socket
);
1820 g_source_set_callback (source
, (GSourceFunc
) nice_agent_g_source_cb
,
1821 ctx
, (GDestroyNotify
) io_ctx_free
);
1822 g_debug ("Agent %p : Attach source %p (stream %u).", agent
, source
, stream
->id
);
1823 g_source_attach (source
, component
->ctx
);
1824 component
->gsources
= g_slist_append (component
->gsources
, source
);
1829 * Attaches socket handles of 'stream' to the main eventloop
1834 priv_attach_stream_component (NiceAgent
*agent
,
1836 Component
*component
)
1840 for (i
= component
->sockets
; i
; i
= i
->next
)
1841 priv_attach_stream_component_socket (agent
, stream
, component
, i
->data
);
1847 * Detaches socket handles of 'stream' from the main eventloop
1851 static void priv_detach_stream_component (Stream
*stream
, Component
*component
)
1855 for (i
= component
->gsources
; i
; i
= i
->next
) {
1856 GSource
*source
= i
->data
;
1857 g_debug ("Detach source %p (stream %u).", source
, stream
->id
);
1858 g_source_destroy (source
);
1861 g_slist_free (component
->gsources
);
1862 component
->gsources
= NULL
;
1865 NICEAPI_EXPORT gboolean
1866 nice_agent_attach_recv (
1871 NiceAgentRecvFunc func
,
1874 Component
*component
= NULL
;
1875 Stream
*stream
= NULL
;
1876 gboolean ret
= FALSE
;
1878 g_static_rec_mutex_lock (&agent
->mutex
);
1880 /* attach candidates */
1882 /* step: check that params specify an existing pair */
1883 if (!agent_find_component (agent
, stream_id
, component_id
, &stream
, &component
)) {
1884 g_warning ("Could not find component %u in stream %u", component_id
,
1889 if (component
->g_source_io_cb
&& func
== NULL
)
1890 priv_detach_stream_component (stream
, component
);
1895 component
->g_source_io_cb
= func
;
1896 component
->data
= data
;
1897 component
->ctx
= ctx
;
1898 priv_attach_stream_component (agent
, stream
, component
);
1900 component
->g_source_io_cb
= NULL
;
1901 component
->data
= NULL
;
1902 component
->ctx
= NULL
;
1907 g_static_rec_mutex_unlock (&agent
->mutex
);
1912 * Sets the selected candidate pair for media transmission
1913 * for given stream component. Calling this function will
1914 * disable all further ICE processing (connection check,
1915 * state machine updates, etc). Note that keepalives will
1916 * continue to be sent.
1918 NICEAPI_EXPORT gboolean
1919 nice_agent_set_selected_pair (
1923 const gchar
*lfoundation
,
1924 const gchar
*rfoundation
)
1926 Component
*component
;
1929 gboolean ret
= FALSE
;
1931 g_static_rec_mutex_lock (&agent
->mutex
);
1933 /* step: check that params specify an existing pair */
1934 if (!agent_find_component (agent
, stream_id
, component_id
, &stream
, &component
)) {
1938 if (!component_find_pair (component
, agent
, lfoundation
, rfoundation
, &pair
)){
1942 /* step: stop connectivity checks (note: for the whole stream) */
1943 conn_check_prune_stream (agent
, stream
);
1945 /* step: change component state */
1946 agent_signal_component_state_change (agent
, stream_id
, component_id
, NICE_COMPONENT_STATE_READY
);
1948 /* step: set the selected pair */
1949 component_update_selected_pair (component
, &pair
);
1950 agent_signal_new_selected_pair (agent
, stream_id
, component_id
, lfoundation
, rfoundation
);
1955 g_static_rec_mutex_unlock (&agent
->mutex
);
1960 guint
agent_timeout_add_with_context (NiceAgent
*agent
, guint interval
,
1961 GSourceFunc function
, gpointer data
)
1966 g_return_val_if_fail (function
!= NULL
, 0);
1968 source
= g_timeout_source_new (interval
);
1970 g_source_set_callback (source
, function
, data
, NULL
);
1971 id
= g_source_attach (source
, agent
->main_context
);
1972 g_source_unref (source
);
1979 * nice_agent_set_selected_remote_candidate:
1980 * @agent: a #NiceAgent
1981 * @stream_id: the stream id
1982 * @component_id: the component id
1983 * @candidate: the #NiceCandidate to force
1985 * Sets the selected remote candidate for media transmission
1986 * for given stream component. Calling this function will
1987 * disable all further ICE processing (connection check,
1988 * state machine updates, etc). Note that keepalives will
1989 * continue to be sent.
1991 * Returns: %TRUE on success, %FALSE on failure
1993 NICEAPI_EXPORT gboolean
1994 nice_agent_set_selected_remote_candidate (
1998 NiceCandidate
*candidate
)
2000 Component
*component
;
2002 NiceCandidate
*lcandidate
= NULL
;
2003 gboolean ret
= FALSE
;
2005 g_static_rec_mutex_lock (&agent
->mutex
);
2007 /* step: check if the component exists*/
2008 if (!agent_find_component (agent
, stream_id
, component_id
, &stream
, &component
)) {
2012 /* step: stop connectivity checks (note: for the whole stream) */
2013 conn_check_prune_stream (agent
, stream
);
2016 /* step: set the selected pair */
2017 lcandidate
= component_set_selected_remote_candidate (agent
, component
,
2022 /* step: change component state */
2023 agent_signal_component_state_change (agent
, stream_id
, component_id
, NICE_COMPONENT_STATE_READY
);
2025 agent_signal_new_selected_pair (agent
, stream_id
, component_id
,
2026 lcandidate
->foundation
,
2027 candidate
->foundation
);
2032 g_static_rec_mutex_unlock (&agent
->mutex
);