|
|
|
@ -33,6 +33,7 @@ struct sdp_connection { |
|
|
|
struct sdp_attributes { |
|
|
|
GQueue list; |
|
|
|
GHashTable *hash; |
|
|
|
GHashTable *lists_hash; |
|
|
|
}; |
|
|
|
|
|
|
|
struct sdp_session { |
|
|
|
@ -64,6 +65,18 @@ struct attribute_rtcp { |
|
|
|
struct network_address address; |
|
|
|
}; |
|
|
|
|
|
|
|
struct attribute_candidate { |
|
|
|
str foundation; |
|
|
|
str component_str; |
|
|
|
str transport; |
|
|
|
str priority_str; |
|
|
|
/* incomplete */ |
|
|
|
|
|
|
|
unsigned long component; |
|
|
|
unsigned long priority; |
|
|
|
int parsed:1; |
|
|
|
}; |
|
|
|
|
|
|
|
struct sdp_attribute { |
|
|
|
str full_line, /* including a= and \r\n */ |
|
|
|
line_value, /* without a= and without \r\n */ |
|
|
|
@ -75,11 +88,13 @@ struct sdp_attribute { |
|
|
|
enum { |
|
|
|
ATTR_OTHER = 0, |
|
|
|
ATTR_RTCP, |
|
|
|
ATTR_CANDIDATE, |
|
|
|
ATTR_ICE, |
|
|
|
} attr; |
|
|
|
|
|
|
|
union { |
|
|
|
struct attribute_rtcp rtcp; |
|
|
|
struct attribute_candidate candidate; |
|
|
|
} u; |
|
|
|
}; |
|
|
|
|
|
|
|
@ -210,6 +225,8 @@ static int parse_media(char *start, char *end, struct sdp_media *output) { |
|
|
|
static void attrs_init(struct sdp_attributes *a) { |
|
|
|
g_queue_init(&a->list); |
|
|
|
a->hash = g_hash_table_new(str_hash, str_equal); |
|
|
|
a->lists_hash = g_hash_table_new_full(str_hash, str_equal, |
|
|
|
NULL, (GDestroyNotify) g_queue_free); |
|
|
|
} |
|
|
|
|
|
|
|
static int parse_attribute_rtcp(struct sdp_attribute *output) { |
|
|
|
@ -236,6 +253,29 @@ static int parse_attribute_rtcp(struct sdp_attribute *output) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int parse_attribute_candidate(struct sdp_attribute *output) { |
|
|
|
char *end, *start, *ep; |
|
|
|
|
|
|
|
start = output->value.s; |
|
|
|
end = start + output->value.len; |
|
|
|
output->attr = ATTR_CANDIDATE; |
|
|
|
|
|
|
|
EXTRACT_TOKEN(u.candidate.foundation); |
|
|
|
EXTRACT_TOKEN(u.candidate.component_str); |
|
|
|
EXTRACT_TOKEN(u.candidate.transport); |
|
|
|
EXTRACT_TOKEN(u.candidate.priority_str); |
|
|
|
|
|
|
|
output->u.candidate.component = strtoul(output->u.candidate.component_str.s, &ep, 10); |
|
|
|
if (ep == output->u.candidate.component_str.s) |
|
|
|
return -1; |
|
|
|
output->u.candidate.priority = strtoul(output->u.candidate.priority_str.s, &ep, 10); |
|
|
|
if (ep == output->u.candidate.priority_str.s) |
|
|
|
return -1; |
|
|
|
|
|
|
|
output->u.candidate.parsed = 1; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void parse_attribute(struct sdp_attribute *a) { |
|
|
|
a->name = a->line_value; |
|
|
|
str_chr_str(&a->value, &a->name, ':'); |
|
|
|
@ -275,7 +315,7 @@ static void parse_attribute(struct sdp_attribute *a) { |
|
|
|
break; |
|
|
|
case 9: |
|
|
|
if (!str_cmp(&a->name, "candidate")) |
|
|
|
a->attr = ATTR_ICE; |
|
|
|
parse_attribute_candidate(a); |
|
|
|
else if (!str_cmp(&a->name, "ice-ufrag")) |
|
|
|
a->attr = ATTR_ICE; |
|
|
|
break; |
|
|
|
@ -302,6 +342,7 @@ int sdp_parse(str *body, GQueue *sessions) { |
|
|
|
struct sdp_attributes *attrs; |
|
|
|
struct sdp_attribute *attr; |
|
|
|
str *adj_s; |
|
|
|
GQueue *attr_queue; |
|
|
|
|
|
|
|
b = body->s; |
|
|
|
end = str_end(body); |
|
|
|
@ -389,6 +430,12 @@ int sdp_parse(str *body, GQueue *sessions) { |
|
|
|
if (attr->key.s) |
|
|
|
g_hash_table_insert(attrs->hash, &attr->key, attr); |
|
|
|
|
|
|
|
attr_queue = g_hash_table_lookup(attrs->lists_hash, &attr->name); |
|
|
|
if (!attr_queue) |
|
|
|
g_hash_table_insert(attrs->lists_hash, &attr->name, |
|
|
|
(attr_queue = g_queue_new())); |
|
|
|
g_queue_push_tail(attr_queue, attr); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
case 'b': |
|
|
|
@ -437,6 +484,7 @@ static void free_attributes(struct sdp_attributes *a) { |
|
|
|
struct sdp_attribute *attr; |
|
|
|
|
|
|
|
g_hash_table_destroy(a->hash); |
|
|
|
g_hash_table_destroy(a->lists_hash); |
|
|
|
while ((attr = g_queue_pop_head(&a->list))) { |
|
|
|
g_slice_free1(sizeof(*attr), attr); |
|
|
|
} |
|
|
|
@ -757,6 +805,7 @@ static int process_session_attributes(struct sdp_chopper *chop, struct sdp_attri |
|
|
|
|
|
|
|
switch (attr->attr) { |
|
|
|
case ATTR_ICE: |
|
|
|
case ATTR_CANDIDATE: |
|
|
|
if (!flags->ice_remove && !flags->ice_force) |
|
|
|
break; |
|
|
|
goto strip; |
|
|
|
@ -787,6 +836,7 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_attribu |
|
|
|
|
|
|
|
switch (attr->attr) { |
|
|
|
case ATTR_ICE: |
|
|
|
case ATTR_CANDIDATE: |
|
|
|
if (!flags->ice_remove && !flags->ice_force) |
|
|
|
break; |
|
|
|
goto strip; |
|
|
|
@ -826,6 +876,39 @@ static int has_rtcp(struct sdp_session *session, struct sdp_media *media) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static unsigned long prio_calc(unsigned int pref) { |
|
|
|
return (1 << 24) * 126 + (1 << 8) * pref + 256 * 1; |
|
|
|
} |
|
|
|
|
|
|
|
static unsigned long new_priority(struct sdp_media *media) { |
|
|
|
str s; |
|
|
|
GQueue *cands; |
|
|
|
unsigned int pref; |
|
|
|
unsigned long prio; |
|
|
|
GList *l; |
|
|
|
struct attribute_candidate *c; |
|
|
|
|
|
|
|
pref = 65535; |
|
|
|
prio = prio_calc(pref); |
|
|
|
|
|
|
|
if (!media) |
|
|
|
goto out; |
|
|
|
|
|
|
|
str_init(&s, "candidate"); |
|
|
|
cands = g_hash_table_lookup(media->attributes.lists_hash, &s); |
|
|
|
|
|
|
|
for (l = cands->head; l; l = l->next) { |
|
|
|
c = l->data; |
|
|
|
while (c->priority >= prio) { |
|
|
|
pref--; |
|
|
|
prio = prio_calc(pref); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
out: |
|
|
|
return prio; |
|
|
|
} |
|
|
|
|
|
|
|
int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call *call, |
|
|
|
enum call_opmode opmode, struct sdp_ng_flags *flags, GHashTable *streamhash) |
|
|
|
{ |
|
|
|
@ -835,6 +918,7 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call *call, |
|
|
|
int off; |
|
|
|
struct stream_input si, *sip; |
|
|
|
struct streamrelay *rtp, *rtcp; |
|
|
|
unsigned long priority; |
|
|
|
|
|
|
|
off = opmode; |
|
|
|
m = call->callstreams->head; |
|
|
|
@ -858,18 +942,15 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call *call, |
|
|
|
|
|
|
|
if (flags->ice_force) { |
|
|
|
/* XXX locking here? */ |
|
|
|
create_random_string(call, &rtp->up->ice_ufrag[0], 8); |
|
|
|
create_random_string(call, &rtp->up->ice_ufrag, 8); |
|
|
|
create_random_string(call, &rtp->up->ice_pwd, 28); |
|
|
|
|
|
|
|
copy_up_to_end_of(chop, &session->s); |
|
|
|
chopper_append_c(chop, "a=ice-lite\r\na=ice-ufrag:"); |
|
|
|
chopper_append_str(chop, &rtp->up->ice_ufrag[0]); |
|
|
|
chopper_append_str(chop, &rtp->up->ice_ufrag); |
|
|
|
chopper_append_c(chop, "\r\na=ice-pwd:"); |
|
|
|
chopper_append_str(chop, &rtp->up->ice_pwd); |
|
|
|
chopper_append_c(chop, "\r\n"); |
|
|
|
|
|
|
|
rtp->stun = 1; |
|
|
|
rtcp->stun = 1; |
|
|
|
} |
|
|
|
|
|
|
|
for (k = session->media_streams.head; k; k = k->next) { |
|
|
|
@ -907,17 +988,25 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call *call, |
|
|
|
chopper_append_c(chop, "\r\n"); |
|
|
|
} |
|
|
|
|
|
|
|
if (flags->ice_force) { |
|
|
|
/* prio = (2^24) * 126 + (2^8) * 65535 + (256 - componentID) */ |
|
|
|
if (!flags->ice_remove) { |
|
|
|
if (flags->ice_force) { |
|
|
|
priority = new_priority(NULL); |
|
|
|
rtp->stun = 1; |
|
|
|
rtcp->stun = 1; |
|
|
|
} |
|
|
|
else |
|
|
|
priority = new_priority(media); |
|
|
|
|
|
|
|
chopper_append_c(chop, "a=candidate:"); |
|
|
|
chopper_append_str(chop, &ice_foundation_str); |
|
|
|
chopper_append_c(chop, " 1 UDP 2130706431 "); |
|
|
|
chopper_append_printf(chop, " 1 UDP %lu ", priority); |
|
|
|
insert_ice_address(chop, flags, rtp); |
|
|
|
chopper_append_c(chop, " typ host\r\n"); |
|
|
|
|
|
|
|
if (has_rtcp(session, media)) { |
|
|
|
chopper_append_c(chop, "a=candidate:"); |
|
|
|
chopper_append_str(chop, &ice_foundation_str); |
|
|
|
chopper_append_c(chop, " 2 UDP 2130706430 "); |
|
|
|
chopper_append_printf(chop, " 2 UDP %lu ", priority - 1); |
|
|
|
insert_ice_address(chop, flags, rtcp); |
|
|
|
chopper_append_c(chop, " typ host\r\n"); |
|
|
|
} |
|
|
|
|