Commit a1862cc2 authored by Kai Vehmanen's avatar Kai Vehmanen
Browse files

Added support for multiple components (e.g. for RTCP support). Updated to...

Added support for multiple components (e.g. for RTCP support). Updated to latest ID-17 ICE spec. Reduce the amount of debugging output.

darcs-hash:20070716095630-77cd4-d955656f1a7bb0cb8ab7b695d76aa91ee5cc7bc5.gz
parent d1ddf1e5
......@@ -127,9 +127,6 @@ agent_find_component (
{
Stream *s;
if (component_id != 1)
return FALSE;
s = agent_find_stream (agent, stream_id);
if (s == NULL)
......@@ -139,7 +136,7 @@ agent_find_component (
*stream = s;
if (component)
*component = s->component;
*component = stream_find_component_by_id (s, component_id);
return TRUE;
}
......@@ -548,6 +545,44 @@ static void priv_signal_component_state_connecting (NiceAgent *agent, guint stre
}
#endif
static gboolean
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)
{
CandidateDiscovery *cdisco;
GSList *modified_list;
/* note: no need to check for redundant candidates, as this is
* done later on in the process */
cdisco = g_slice_new0 (CandidateDiscovery);
if (cdisco) {
modified_list = g_slist_append (agent->discovery_list, cdisco);
if (modified_list) {
agent->discovery_list = modified_list;
cdisco->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
cdisco->socket = host_candidate->sockptr->fileno;
cdisco->nicesock = host_candidate->sockptr;
cdisco->server_addr = stun_server_ip;
cdisco->server_port = stun_server_port;
cdisco->interface = addr;
cdisco->stream = stream;
cdisco->component = stream_find_component_by_id (stream, component_id);
cdisco->agent = agent;
g_debug ("Adding new srv-rflx candidate discovery %p\n", cdisco);
modified_list = g_slist_append (agent->discovery_list, cdisco);
if (modified_list) {
agent->discovery_list = modified_list;
++agent->discovery_unsched_items;
}
}
return TRUE;
}
return FALSE;
}
/**
* nice_agent_add_stream:
* @agent: a NiceAgent
......@@ -566,13 +601,12 @@ nice_agent_add_stream (
{
Stream *stream;
GSList *i, *modified_list = NULL;
g_assert (n_components == 1);
guint n;
if (!agent->local_addresses)
return 0;
stream = stream_new ();
stream = stream_new (n_components);
if (stream) {
modified_list = g_slist_append (agent->streams, stream);
if (modified_list) {
......@@ -600,56 +634,39 @@ nice_agent_add_stream (
for (i = agent->local_addresses; i; i = i->next)
{
NiceAddress *addr = i->data;
CandidateDiscovery *cand;
NiceCandidate *host_candidate;
/* XXX: not multi-component ready */
host_candidate = discovery_add_local_host_candidate (agent, stream->id,
stream->component->id, addr);
for (n = 0; n < n_components; n++) {
host_candidate = discovery_add_local_host_candidate (agent, stream->id,
n + 1, addr);
if (!host_candidate) {
stream->id = 0;
break;
}
if (agent->full_mode &&
agent->stun_server_ip) {
if (!host_candidate) {
stream->id = 0;
break;
}
/* note: no need to check for redundant candidates, as this is
* done later on in the process */
cand = g_slice_new0 (CandidateDiscovery);
if (cand) {
modified_list = g_slist_append (agent->discovery_list, cand);
if (modified_list) {
agent->discovery_list = modified_list;
cand->type = NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE;
cand->socket = host_candidate->sockptr->fileno;
cand->nicesock = host_candidate->sockptr;
cand->server_addr = agent->stun_server_ip;
cand->server_port = agent->stun_server_port;
cand->interface = addr;
cand->stream = stream;
/* XXX: not multi-component ready */
cand->component = stream->component;
cand->agent = agent;
g_debug ("Adding new srv-rflx candidate %p\n", cand);
modified_list = g_slist_append (agent->discovery_list, cand);
if (modified_list) {
agent->discovery_list = modified_list;
++agent->discovery_unsched_items;
}
if (agent->full_mode &&
agent->stun_server_ip) {
gboolean res =
priv_add_srv_rfx_candidate_discovery (agent,
host_candidate,
agent->stun_server_ip,
agent->stun_server_port,
stream,
n + 1 /* component-id */,
addr);
if (res != TRUE) {
/* note: memory allocation failure, return error */
stream->id = 0;
break;
}
}
else {
/* note: memory allocation failure, return error */
stream->id = 0;
break;
}
}
}
/* step: attach the newly created sockets to the mainloop
* context */
if (agent->main_context_set && stream->id > 0)
......@@ -1102,7 +1119,7 @@ nice_agent_recv (
socket = component_find_udp_socket_by_fd (component, j);
g_assert (socket);
len = _nice_agent_recv (agent, stream, component, socket,
buf_len, buf);
......@@ -1136,8 +1153,7 @@ nice_agent_recv_sock (
socket = component_find_udp_socket_by_fd (component, sock);
g_assert (socket);
/* XXX: not multi-component ready */
return _nice_agent_recv (agent, stream, stream->component,
return _nice_agent_recv (agent, stream, component,
socket, buf_len, buf);
}
......@@ -1172,18 +1188,21 @@ nice_agent_poll_read (
for (i = agent->streams; i; i = i->next)
{
GSList *j;
GSList *j, *k;
Stream *stream = i->data;
/* XXX: not multi-component ready */
Component *component = stream->component;
for (j = component->sockets; j; j = j->next)
{
NiceUDPSocket *sockptr = j->data;
FD_SET (sockptr->fileno, &fds);
max_fd = MAX (sockptr->fileno, max_fd);
}
for (k = stream->components; k; k = k->next)
{
Component *component = k->data;
for (j = component->sockets; j; j = j->next)
{
NiceUDPSocket *sockptr = j->data;
FD_SET (sockptr->fileno, &fds);
max_fd = MAX (sockptr->fileno, max_fd);
}
}
}
for (i = other_fds; i; i = i->next)
......@@ -1216,31 +1235,32 @@ nice_agent_poll_read (
{
NiceUDPSocket *socket = NULL;
Stream *stream = NULL;
Component *component = NULL;
gchar buf[MAX_STUN_DATAGRAM_PAYLOAD];
guint len;
for (i = agent->streams; i; i = i->next)
{
Stream *s = i->data;
Component *c = s->component;
Component *c = stream_find_component_by_fd (s, j);
socket = component_find_udp_socket_by_fd (c, j);
if (socket != NULL) {
stream = s;
component = c;
break;
}
}
if (socket == NULL || stream == NULL)
if (socket == NULL || stream == NULL || component == NULL)
break;
/* XXX: not multi-component ready */
len = _nice_agent_recv (agent, stream, stream->component,
len = _nice_agent_recv (agent, stream, component,
socket, MAX_STUN_DATAGRAM_PAYLOAD, buf);
if (len && func != NULL)
func (agent, stream->id, stream->component->id, len, buf,
func (agent, stream->id, component->id, len, buf,
data);
}
}
......@@ -1268,13 +1288,7 @@ nice_agent_send (
Stream *stream;
Component *component;
/* note: dear compiler, these are for you: */
(void)component_id;
stream = agent_find_stream (agent, stream_id);
/* XXX: not multi-component ready */
component = stream->component;
agent_find_component (agent, stream_id, component_id, &stream, &component);
if (component->selected_pair.local != NULL)
{
......@@ -1475,30 +1489,32 @@ nice_agent_g_source_cb (
*/
static gboolean priv_attach_new_stream (NiceAgent *agent, Stream *stream)
{
GSList *j;
/* XXX: not multi-component ready */
Component *component = stream->component;
for (j = component->sockets; j; j = j->next) {
NiceUDPSocket *udp_socket = j->data;
GIOChannel *io;
GSource *source;
IOCtx *ctx;
GSList *modified_list;
GSList *i, *j;
for (i = stream->components; i; i = i->next) {
Component *component = i->data;
for (j = component->sockets; j; j = j->next) {
NiceUDPSocket *udp_socket = j->data;
GIOChannel *io;
GSource *source;
IOCtx *ctx;
GSList *modified_list;
io = g_io_channel_unix_new (udp_socket->fileno);
source = g_io_create_watch (io, G_IO_IN);
ctx = io_ctx_new (agent, stream, component, udp_socket);
g_source_set_callback (source, (GSourceFunc) nice_agent_g_source_cb,
ctx, (GDestroyNotify) io_ctx_free);
g_debug ("Attach source %p (stream %u).", source, stream->id);
g_source_attach (source, NULL);
modified_list = g_slist_append (component->gsources, source);
if (!modified_list) {
g_source_destroy (source);
return FALSE;
io = g_io_channel_unix_new (udp_socket->fileno);
source = g_io_create_watch (io, G_IO_IN);
ctx = io_ctx_new (agent, stream, component, udp_socket);
g_source_set_callback (source, (GSourceFunc) nice_agent_g_source_cb,
ctx, (GDestroyNotify) io_ctx_free);
g_debug ("Attach source %p (stream %u).", source, stream->id);
g_source_attach (source, NULL);
modified_list = g_slist_append (component->gsources, source);
if (!modified_list) {
g_source_destroy (source);
return FALSE;
}
component->gsources = modified_list;
}
component->gsources = modified_list;
}
return TRUE;
......@@ -1512,18 +1528,20 @@ static gboolean priv_attach_new_stream (NiceAgent *agent, Stream *stream)
*/
static void priv_deattach_stream (Stream *stream)
{
GSList *j;
/* XXX: not multi-component ready */
Component *component = stream->component;
for (j = component->gsources; j; j = j->next) {
GSource *source = j->data;
g_debug ("Detach source %p (stream %u).", source, stream->id);
g_source_destroy (source);
}
GSList *i, *j;
for (i = stream->components; i; i = i->next) {
Component *component = i->data;
for (j = component->gsources; j; j = j->next) {
GSource *source = j->data;
g_debug ("Detach source %p (stream %u).", source, stream->id);
g_source_destroy (source);
}
g_slist_free (component->gsources),
g_slist_free (component->gsources),
component->gsources = NULL;
}
}
NICEAPI_EXPORT gboolean
......
......@@ -48,7 +48,8 @@ G_BEGIN_DECLS
#define NICE_CANDIDATE_TYPE_PREF_SERVER_REFLEXIVE 100
#define NICE_CANDIDATE_TYPE_PREF_RELAYED 60
#define NICE_CANDIDATE_MAX_FOUNDATION 16
/* Max foundation size '1*32ice-char' ICE ID-17 */
#define NICE_CANDIDATE_MAX_FOUNDATION 32+1
typedef enum
{
......
......@@ -49,12 +49,12 @@
Component *
component_new (
G_GNUC_UNUSED
ComponentType type)
guint id)
{
Component *component;
component = g_slice_new0 (Component);
component->id = 1;
component->id = id;
return component;
}
......@@ -93,6 +93,12 @@ component_free (Component *cmp)
g_slice_free (Component, cmp);
}
/**
* Returns a component UDP socket struct that uses handle 'fd'.
*
* Note: there might be multiple sockets using the same
* handle.
*/
NiceUDPSocket *
component_find_udp_socket_by_fd (Component *component, guint fd)
{
......
......@@ -46,19 +46,12 @@
G_BEGIN_DECLS
/* (ICE-13 §4.1.1) For RTP-based media streams, the RTP itself has a component
/* (ICE-16 §4.1.1.1) For RTP-based media streams, the RTP itself has a component
* ID of 1, and RTCP a component ID of 2. If an agent is using RTCP it MUST
* obtain a candidate for it. If an agent is using both RTP and RTCP, it
* would end up with 2*K host candidates if an agent has K interfaces.
*/
typedef enum
{
COMPONENT_TYPE_RTP,
COMPONENT_TYPE_RTCP,
} ComponentType;
typedef struct _Component Component;
typedef struct _CandidatePair CandidatePair;
......@@ -71,7 +64,7 @@ struct _CandidatePair
struct _Component
{
ComponentType type;
NiceComponentType type;
guint id;
NiceComponentState state;
GSList *local_candidates; /**< list of Candidate objs */
......@@ -87,7 +80,7 @@ struct _Component
Component *
component_new (
G_GNUC_UNUSED
ComponentType type);
guint component_id);
void
component_free (Component *cmp);
......
......@@ -91,6 +91,10 @@ static CandidateCheckPair *priv_conn_check_find_next_waiting (GSList *conn_check
*/
static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *pair)
{
/* XXX: from ID-16 onwards, the checks should not be sent
* immediately, but be put into the "triggered queue",
* see "7.2.1.4 Triggered Checks"
*/
g_get_current_time (&pair->next_tick);
g_time_val_add (&pair->next_tick, agent->timer_ta * 10);
pair->state = NICE_CHECK_IN_PROGRESS;
......@@ -100,33 +104,57 @@ static gboolean priv_conn_check_initiate (NiceAgent *agent, CandidateCheckPair *
/**
* Unfreezes the next connectivity check in the list. Follows the
* algorithm defined in 5.7.4 of the ICE spec (-15).
* algorithm (2.) defined in 5.7.4 (Computing States) of the ICE spec
* (ID-17), with some exceptions (see comments in code).
*
* See also sect 7.1.2.2.3 (Updating Pair States), and
* priv_conn_check_unfreeze_related().
*
* @return TRUE on success, and FALSE if no frozen candidates were found.
*/
static gboolean priv_conn_check_unfreeze_next (GSList *conncheck_list)
static gboolean priv_conn_check_unfreeze_next (NiceAgent *agent, GSList *conncheck_list)
{
CandidateCheckPair *pair = NULL;
guint64 max_priority = 0;
guint64 max_frozen_priority = 0;
GSList *i;
int c;
guint c;
guint max_components = 0;
gboolean frozen = FALSE;
/* step: calculate the max number of components across streams */
for (i = agent->streams; i; i = i->next) {
Stream *stream = i->data;
if (stream->n_components > max_components)
max_components = stream->n_components;
}
/* note: the pair list is already sorted so separate prio check
* is not needed */
for (c = NICE_COMPONENT_TYPE_RTP; (pair == NULL) && c < NICE_COMPONENT_TYPE_RTCP; c++) {
/* XXX: the unfreezing is implemented a bit differently than in the
* current ICE spec, but should still be interoperate:
* - checks are not grouped by foundation
* - one frozen check is unfrozen (lowest component-id, highest
* priority)
*/
for (c = 0; (pair == NULL) && c < max_components; c++) {
for (i = conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->state == NICE_CHECK_FROZEN &&
p->priority > max_priority) {
max_priority = p->priority;
pair = p;
if (p->component_id == c + 1) {
if (p->state == NICE_CHECK_FROZEN) {
frozen = TRUE;
if (p->priority > max_frozen_priority) {
max_frozen_priority = p->priority;
pair = p;
}
}
}
}
}
if (pair) {
g_debug ("Pair %p (%s) unfrozen.", pair, pair->foundation);
g_debug ("Pair %p with s/c-id %u/%u (%s) unfrozen.", pair, pair->stream_id, pair->component_id, pair->foundation);
pair->state = NICE_CHECK_WAITING;
return TRUE;
}
......@@ -134,6 +162,69 @@ static gboolean priv_conn_check_unfreeze_next (GSList *conncheck_list)
return FALSE;
}
/**
* Unfreezes the next next connectivity check in the list after
* check 'success_check' has succesfully completed.
*
* See sect 7.1.2.2.3 (Updating Pair States) of ICE spec (ID-17).
*
* @param agent context
* @param ok_check a connectivity check that has just completed
*
* @return TRUE on success, and FALSE if no frozen candidates were found.
*/
static void priv_conn_check_unfreeze_related (NiceAgent *agent, CandidateCheckPair *ok_check)
{
GSList *i, *j;
Stream *stream;
guint unfrozen = 0;
g_assert (ok_check);
g_assert (ok_check->state == NICE_CHECK_SUCCEEDED);
/* step: perform the step (1) of 'Updating Pair States' */
for (i = agent->conncheck_list; i ; i = i->next) {
CandidateCheckPair *p = i->data;
if (p->stream_id == ok_check->stream_id) {
if (p->state == NICE_CHECK_FROZEN &&
strcmp (p->foundation, ok_check->foundation) == 0) {
g_debug ("Unfreezing check %p (related to %p).", p, ok_check);
p->state = NICE_CHECK_WAITING;
}
}
}
/* step: perform the step (2) of 'Updating Pair States' */
stream = agent_find_stream (agent, ok_check->stream_id);
if (stream_all_components_ready (stream)) {
/* step: unfreeze checks from other streams */
for (i = agent->streams; i ; i = i->next) {
Stream *s = i->data;
for (j = agent->conncheck_list; j ; j = j->next) {
CandidateCheckPair *p = j->data;
if (p->stream_id == s->id &&
p->stream_id != ok_check->stream_id) {
if (p->state == NICE_CHECK_FROZEN &&
strcmp (p->foundation, ok_check->foundation) == 0) {
g_debug ("Unfreezing check %p from stream %u (related to %p).", p, s->id, ok_check);
p->state = NICE_CHECK_WAITING;
++unfrozen;
}
}
}
/* note: only unfreeze check from one stream at a time */
if (unfrozen)
break;
}
}
if (unfrozen == 0)
priv_conn_check_unfreeze_next (agent, agent->conncheck_list);
}
/**
* Timer callback that handles initiating and managing connectivity
* checks (paced by the Ta timer).
......@@ -161,13 +252,6 @@ static gboolean priv_conn_check_tick (gpointer pointer)
}
#endif
if (!pair) {
gboolean c = priv_conn_check_unfreeze_next (agent->conncheck_list);
if (c == TRUE) {
pair = priv_conn_check_find_next_waiting (agent->conncheck_list);
}
}
if (pair) {
priv_conn_check_initiate (agent, pair);
keep_timer_going = TRUE;
......@@ -233,6 +317,10 @@ static gboolean priv_conn_check_tick (gpointer pointer)
(succeeded && nominated == 0))
keep_timer_going = TRUE;
/* note: if no waiting checks, unfreeze one check */
if (!waiting && frozen)
priv_conn_check_unfreeze_next (agent, agent->conncheck_list);
/* step: nominate some candidate
* - no work left but still no nominated checks (possibly
* caused by a controlling-controlling role conflict)
......@@ -280,7 +368,7 @@ static gboolean priv_conn_check_tick (gpointer pointer)
static gboolean priv_conn_keepalive_tick (gpointer pointer)
{
NiceAgent *agent = pointer;
GSList *i;
GSList *i, *j;
int errors = 0;
g_debug ("%s", G_STRFUNC);
......@@ -288,27 +376,29 @@ static gboolean priv_conn_keepalive_tick (gpointer pointer)
/* case 1: session established and media flowing
* (ref ICE sect 10 ID-16) */
for (i = agent->streams; i; i = i->next) {
/* XXX: not multi-component ready */
Stream *stream = i->data;
Component *component = stream->component;
if (component->selected_pair.local != NULL &&
component->media_after_tick != TRUE) {
CandidatePair *p = &component->selected_pair;
struct sockaddr sockaddr;
int res;
memset (&sockaddr, 0, sizeof (sockaddr));
nice_address_copy_to_sockaddr (&p->remote->addr, &sockaddr);
res = stun_bind_keepalive (p->local->sockptr->fileno,
&sockaddr, sizeof (sockaddr));
g_debug ("stun_bind_keepalive for pair %p res %d.", p, res);
if (res < 0)
++errors;
for (j = stream->components; j; j = j->next) {
Component *component = j->data;
if (component->selected_pair.local != NULL &&
component->media_after_tick != TRUE) {
CandidatePair *p = &component->selected_pair;