diff --git a/daemon/call.c b/daemon/call.c index 4c62fcab7..a4462dd04 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2187,6 +2187,26 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, GQueue *streams, ben } } +static unsigned int stream_hash(struct stream_input *s) { + unsigned int ret, *p; + + ret = s->stream.port; + p = (void *) &s->stream.ip46; + while (((void *) p) < (((void *) &s->stream.ip46) + sizeof(s->stream.ip46))) { + ret ^= *p; + p++; + } + return ret; +} + +static int stream_equal(struct stream_input *a, struct stream_input *b) { + if (a->stream.port != b->stream.port) + return 0; + if (memcmp(&a->stream.ip46, &b->stream.ip46, sizeof(a->stream.ip46))) + return 0; + return 1; +} + static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output, enum call_opmode opmode, const char *tagname) { str sdp, fromtag, viabranch, callid; char *errstr; @@ -2196,6 +2216,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster int ret, num; struct sdp_ng_flags flags; struct sdp_chopper *chopper; + GHashTable *streamhash; if (!bencode_dictionary_get_str(input, "sdp", &sdp)) return "No SDP body in message"; @@ -2209,8 +2230,9 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster if (sdp_parse(&sdp, &parsed)) return "Failed to parse SDP"; + streamhash = g_hash_table_new((GHashFunc) stream_hash, (GEqualFunc) stream_equal); errstr = "Incomplete SDP specification"; - if (sdp_streams(&parsed, &streams)) + if (sdp_streams(&parsed, &streams, streamhash)) goto out; call_ng_process_flags(&flags, &streams, input); @@ -2224,7 +2246,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster chopper = sdp_chopper_new(&sdp); bencode_buffer_destroy_add(output->buffer, (free_func_t) sdp_chopper_destroy, chopper); num = call_streams(call, &streams, &fromtag, opmode); - ret = sdp_replace(chopper, &parsed, call, (num >= 0) ? opmode : (opmode ^ 1), &flags); + ret = sdp_replace(chopper, &parsed, call, (num >= 0) ? opmode : (opmode ^ 1), &flags, streamhash); mutex_unlock(&call->lock); obj_put(call); @@ -2241,6 +2263,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster out: sdp_free(&parsed); streams_free(&streams); + g_hash_table_destroy(streamhash); log_info = NULL; return errstr; diff --git a/daemon/sdp.c b/daemon/sdp.c index fa0ee44fa..86b6dab66 100644 --- a/daemon/sdp.c +++ b/daemon/sdp.c @@ -296,13 +296,27 @@ void sdp_free(GQueue *sessions) { } } -int sdp_streams(const GQueue *sessions, GQueue *streams) { +static int fill_stream(struct stream_input *si, struct sdp_media *media, struct sdp_session *session, int offset) { + if (media->connection.parsed) + si->stream.ip46 = media->connection.address.parsed; + else if (session->connection.parsed) + si->stream.ip46 = session->connection.address.parsed; + else + return -1; + + /* we ignore the media type */ + si->stream.port = (media->port_num + (offset * 2)) & 0xffff; + + return 0; +} + +int sdp_streams(const GQueue *sessions, GQueue *streams, GHashTable *streamhash) { struct sdp_session *session; struct sdp_media *media; struct stream_input *si; GList *l, *k; const char *errstr; - int i, num, cons_num; + int i, num; num = 0; for (l = sessions->head; l; l = l->next) { @@ -311,24 +325,22 @@ int sdp_streams(const GQueue *sessions, GQueue *streams) { for (k = session->media_streams.head; k; k = k->next) { media = k->data; - cons_num = media->port_count; for (i = 0; i < media->port_count; i++) { si = g_slice_alloc0(sizeof(*si)); errstr = "No address info found for stream"; - if (media->connection.parsed) - si->stream.ip46 = media->connection.address.parsed; - else if (session->connection.parsed) - si->stream.ip46 = session->connection.address.parsed; - else + if (fill_stream(si, media, session, i)) goto error; - /* we ignore the media type */ - si->stream.port = (media->port_num + (i * 2)) & 0xffff; + if (i == 0 && g_hash_table_contains(streamhash, si)) { + g_slice_free1(sizeof(*si), si); + continue; + } + si->stream.num = ++num; - si->consecutive_num = cons_num; - cons_num = 1; + si->consecutive_num = (i == 0) ? media->port_count : 1; + g_hash_table_insert(streamhash, si, si); g_queue_push_tail(streams, si); } } @@ -499,14 +511,14 @@ void sdp_chopper_destroy(struct sdp_chopper *chop) { } /* XXX use stream numbers as index */ -/* XXX use port numbers as index */ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call *call, - enum call_opmode opmode, struct sdp_ng_flags *flags) + enum call_opmode opmode, struct sdp_ng_flags *flags, GHashTable *streamhash) { struct sdp_session *session; struct sdp_media *media; GList *l, *k, *m; int off, skip; + struct stream_input si, *sip; off = opmode; m = call->callstreams->head; @@ -526,6 +538,19 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call *call, for (k = session->media_streams.head; k; k = k->next) { media = k->data; + if (fill_stream(&si, media, session, 0)) + goto error; + + sip = g_hash_table_lookup(streamhash, &si); + if (!sip) + goto error; + if (!m) + m = call->callstreams->head; + while (m && ((struct callstream *) m->data)->num < sip->stream.num) + m = m->next; + while (m && ((struct callstream *) m->data)->num > sip->stream.num) + m = m->prev; + skip = replace_media_port(chop, media, m, off); if (skip < 0) goto error; diff --git a/daemon/sdp.h b/daemon/sdp.h index 788ce5eff..c73cb4124 100644 --- a/daemon/sdp.h +++ b/daemon/sdp.h @@ -27,9 +27,9 @@ struct sdp_chopper { }; int sdp_parse(str *body, GQueue *sessions); -int sdp_streams(const GQueue *sessions, GQueue *streams); +int sdp_streams(const GQueue *sessions, GQueue *streams, GHashTable *); void sdp_free(GQueue *sessions); -int sdp_replace(struct sdp_chopper *, GQueue *, struct call *, enum call_opmode, struct sdp_ng_flags *); +int sdp_replace(struct sdp_chopper *, GQueue *, struct call *, enum call_opmode, struct sdp_ng_flags *, GHashTable *); struct sdp_chopper *sdp_chopper_new(str *input); void sdp_chopper_destroy(struct sdp_chopper *chop);