Browse Source

TT#30404 accept and reject codecs according to transcoding preference

Change-Id: I7e4d1d834289433ae4a42d78b92cbc745884d5d1
changes/17/18617/11
Richard Fuchs 8 years ago
parent
commit
a4e73c90e8
6 changed files with 160 additions and 54 deletions
  1. +9
    -7
      daemon/call.c
  2. +9
    -4
      daemon/call.h
  3. +136
    -37
      daemon/codec.c
  4. +1
    -1
      daemon/recording.c
  5. +4
    -4
      daemon/redis.c
  6. +1
    -1
      daemon/ssrc.c

+ 9
- 7
daemon/call.c View File

@ -683,8 +683,9 @@ static struct call_media *__get_media(struct call_monologue *ml, GList **it, con
med->call = ml->call; med->call = ml->call;
med->index = sp->index; med->index = sp->index;
call_str_cpy(ml->call, &med->type, &sp->type); call_str_cpy(ml->call, &med->type, &sp->type);
med->codecs = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, (GDestroyNotify) payload_type_free);
med->codec_names = g_hash_table_new_full(str_hash, str_equal, NULL, (void (*)(void*)) g_queue_free);
med->codecs_recv = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, NULL);
med->codec_names_recv = g_hash_table_new_full(str_hash, str_equal, NULL, (void (*)(void*)) g_queue_free);
med->codec_names_send = g_hash_table_new_full(str_hash, str_equal, NULL, (void (*)(void*)) g_queue_free);
g_queue_push_tail(&ml->medias, med); g_queue_push_tail(&ml->medias, med);
@ -1019,7 +1020,7 @@ static int __init_streams(struct call_media *A, struct call_media *B, const stru
a->rtp_sink = b; a->rtp_sink = b;
PS_SET(a, RTP); /* XXX technically not correct, could be udptl too */ PS_SET(a, RTP); /* XXX technically not correct, could be udptl too */
__rtp_stats_update(a->rtp_stats, A->codecs);
__rtp_stats_update(a->rtp_stats, A->codecs_recv);
if (sp) { if (sp) {
__fill_stream(a, &sp->rtp_endpoint, port_off, sp); __fill_stream(a, &sp->rtp_endpoint, port_off, sp);
@ -1720,7 +1721,7 @@ const struct rtp_payload_type *__rtp_stats_codec(struct call_media *m) {
if (atomic64_get(&rtp_s->packets) == 0) if (atomic64_get(&rtp_s->packets) == 0)
goto out; goto out;
rtp_pt = rtp_payload_type(rtp_s->payload_type, m->codecs);
rtp_pt = rtp_payload_type(rtp_s->payload_type, m->codecs_recv);
out: out:
g_list_free(values); g_list_free(values);
@ -1983,9 +1984,10 @@ static void __call_free(void *p) {
crypto_params_cleanup(&md->sdes_out.params); crypto_params_cleanup(&md->sdes_out.params);
g_queue_clear(&md->streams); g_queue_clear(&md->streams);
g_queue_clear(&md->endpoint_maps); g_queue_clear(&md->endpoint_maps);
g_hash_table_destroy(md->codecs);
g_hash_table_destroy(md->codec_names);
g_queue_clear(&md->codecs_prefs_recv);
g_hash_table_destroy(md->codecs_recv);
g_hash_table_destroy(md->codec_names_recv);
g_hash_table_destroy(md->codec_names_send);
g_queue_clear_full(&md->codecs_prefs_recv, (GDestroyNotify) payload_type_free);
g_queue_clear_full(&md->codecs_prefs_send, (GDestroyNotify) payload_type_free); g_queue_clear_full(&md->codecs_prefs_send, (GDestroyNotify) payload_type_free);
codec_handlers_free(md); codec_handlers_free(md);
g_slice_free1(sizeof(*md), md); g_slice_free1(sizeof(*md), md);


+ 9
- 4
daemon/call.h View File

@ -326,10 +326,15 @@ struct call_media {
GQueue streams; /* normally RTP + RTCP */ GQueue streams; /* normally RTP + RTCP */
GQueue endpoint_maps; GQueue endpoint_maps;
GHashTable *codecs; // int payload type -> struct rtp_payload_type; storage container
GHashTable *codec_names; // codec name -> GQueue of int payload types; storage container
GQueue codecs_prefs_recv, // preference by order in SDP; values shared with 'codecs'
codecs_prefs_send; // ditto for outgoing media; storage container
// what we say we can receive (outgoing SDP):
GHashTable *codecs_recv; // int payload type -> struct rtp_payload_type
GHashTable *codec_names_recv; // codec name -> GQueue of int payload types; storage container
GQueue codecs_prefs_recv; // preference by order in SDP; storage container
// what we can send, taken from received SDP:
GHashTable *codec_names_send; // codec name -> GQueue of int payload types; storage container
GQueue codecs_prefs_send; // storage container
GHashTable *codec_handlers; // int payload type -> struct codec_handler GHashTable *codec_handlers; // int payload type -> struct codec_handler
// XXX combine this with 'codecs' hash table? // XXX combine this with 'codecs' hash table?
volatile struct codec_handler *codec_handler_cache; volatile struct codec_handler *codec_handler_cache;


+ 136
- 37
daemon/codec.c View File

@ -31,6 +31,9 @@ static void __ssrc_handler_free(struct codec_ssrc_handler *p);
static void __transcode_packet_free(struct transcode_packet *); static void __transcode_packet_free(struct transcode_packet *);
static struct rtp_payload_type *__rtp_payload_type_copy(struct rtp_payload_type *pt);
static void __rtp_payload_type_add_name(GHashTable *, struct rtp_payload_type *pt);
static struct codec_handler codec_handler_stub = { static struct codec_handler codec_handler_stub = {
.source_pt.payload_type = -1, .source_pt.payload_type = -1,
@ -83,6 +86,22 @@ static void __make_transcoder(struct codec_handler *handler, struct rtp_payload_
} }
static void __ensure_codec_def(struct rtp_payload_type *pt) {
if (!pt->codec_def)
pt->codec_def = codec_find(&pt->encoding);
}
static GList *__delete_receiver_codec(struct call_media *receiver, GList *link) {
struct rtp_payload_type *pt = link->data;
g_hash_table_remove(receiver->codecs_recv, &pt->payload_type);
g_hash_table_remove(receiver->codec_names_recv, &pt->encoding);
GList *next = link->next;
g_queue_delete_link(&receiver->codecs_prefs_recv, link);
payload_type_free(pt);
return next;
}
// call must be locked in W // call must be locked in W
void codec_handlers_update(struct call_media *receiver, struct call_media *sink) { void codec_handlers_update(struct call_media *receiver, struct call_media *sink) {
if (!receiver->codec_handlers) if (!receiver->codec_handlers)
@ -100,8 +119,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink)
struct rtp_payload_type *pref_dest_codec = NULL; struct rtp_payload_type *pref_dest_codec = NULL;
for (GList *l = sink->codecs_prefs_send.head; l; l = l->next) { for (GList *l = sink->codecs_prefs_send.head; l; l = l->next) {
struct rtp_payload_type *pt = l->data; struct rtp_payload_type *pt = l->data;
if (!pt->codec_def)
pt->codec_def = codec_find(&pt->encoding);
__ensure_codec_def(pt);
if (!pt->codec_def) // not supported, next if (!pt->codec_def) // not supported, next
continue; continue;
ilog(LOG_DEBUG, "Default sink codec is " STR_FORMAT, STR_FMT(&pt->encoding)); ilog(LOG_DEBUG, "Default sink codec is " STR_FORMAT, STR_FMT(&pt->encoding));
@ -109,9 +127,71 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink)
break; break;
} }
for (GList *l = receiver->codecs_prefs_recv.head; l; l = l->next) {
if (MEDIA_ISSET(sink, TRANSCODE)) {
// if the other side is transcoding, we need to accept codecs that were
// originally offered (recv->send) if we support them, even if the
// response (sink->send) doesn't include them
GList *insert_pos = NULL;
for (GList *l = receiver->codecs_prefs_send.head; l; l = l->next) {
struct rtp_payload_type *pt = l->data;
__ensure_codec_def(pt);
if (!pt->codec_def)
continue;
if (g_hash_table_lookup(receiver->codecs_recv, &pt->payload_type)) {
// already present.
// to keep the order intact, we seek the list for the position
// of this codec entry. all newly added codecs must come after
// this entry.
if (!insert_pos)
insert_pos = receiver->codecs_prefs_recv.head;
while (insert_pos) {
if (!insert_pos->next)
break; // end of list - we insert everything after
struct rtp_payload_type *test_pt = insert_pos->data;
if (test_pt->payload_type == pt->payload_type)
break;
insert_pos = insert_pos->next;
}
continue;
}
ilog(LOG_DEBUG, "Accepting offered codec " STR_FORMAT " due to transcoding",
STR_FMT(&pt->encoding));
MEDIA_SET(receiver, TRANSCODE);
// we need a new pt entry
pt = __rtp_payload_type_copy(pt);
// this somewhat duplicates __rtp_payload_type_add_recv
g_hash_table_insert(receiver->codecs_recv, &pt->payload_type, pt);
__rtp_payload_type_add_name(receiver->codec_names_recv, pt);
if (!insert_pos) {
g_queue_push_head(&receiver->codecs_prefs_recv, pt);
insert_pos = receiver->codecs_prefs_recv.head;
}
else {
g_queue_insert_after(&receiver->codecs_prefs_recv, insert_pos, pt);
insert_pos = insert_pos->next;
}
}
}
for (GList *l = receiver->codecs_prefs_recv.head; l; ) {
struct rtp_payload_type *pt = l->data; struct rtp_payload_type *pt = l->data;
if (MEDIA_ISSET(sink, TRANSCODE)) {
// if the other side is transcoding, we may come across a receiver entry
// (recv->recv) that wasn't originally offered (recv->send). we must eliminate
// those
// XXX sufficient to check against payload type?
if (!g_hash_table_lookup(receiver->codec_names_send, &pt->encoding)) {
ilog(LOG_DEBUG, "Eliminating transcoded codec " STR_FORMAT,
STR_FMT(&pt->encoding));
l = __delete_receiver_codec(receiver, l);
continue;
}
}
// first, make sure we have a codec_handler struct for this // first, make sure we have a codec_handler struct for this
struct codec_handler *handler; struct codec_handler *handler;
handler = g_hash_table_lookup(receiver->codec_handlers, &pt->payload_type); handler = g_hash_table_lookup(receiver->codec_handlers, &pt->payload_type);
@ -122,6 +202,9 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink)
handler); handler);
} }
// check our own support for this codec
__ensure_codec_def(pt);
// if the sink's codec preferences are unknown (empty), or there are // if the sink's codec preferences are unknown (empty), or there are
// no supported codecs to transcode to, then we have nothing // no supported codecs to transcode to, then we have nothing
// to do. most likely this is an initial offer without a received answer. // to do. most likely this is an initial offer without a received answer.
@ -129,21 +212,43 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink)
if (!pref_dest_codec) { if (!pref_dest_codec) {
ilog(LOG_DEBUG, "No known/supported sink codec for " STR_FORMAT, STR_FMT(&pt->encoding)); ilog(LOG_DEBUG, "No known/supported sink codec for " STR_FORMAT, STR_FMT(&pt->encoding));
__make_passthrough(handler); __make_passthrough(handler);
continue;
goto next;
} }
if (g_hash_table_lookup(sink->codec_names, &pt->encoding)) {
if (g_hash_table_lookup(sink->codec_names_send, &pt->encoding)) {
// the sink supports this codec. forward without transcoding. // the sink supports this codec. forward without transcoding.
// XXX check format parameters as well // XXX check format parameters as well
ilog(LOG_DEBUG, "Sink supports codec " STR_FORMAT, STR_FMT(&pt->encoding)); ilog(LOG_DEBUG, "Sink supports codec " STR_FORMAT, STR_FMT(&pt->encoding));
__make_passthrough(handler); __make_passthrough(handler);
continue;
goto next;
} }
// the sink does not support this codec -> transcode // the sink does not support this codec -> transcode
ilog(LOG_DEBUG, "Sink does not support codec " STR_FORMAT, STR_FMT(&pt->encoding)); ilog(LOG_DEBUG, "Sink does not support codec " STR_FORMAT, STR_FMT(&pt->encoding));
MEDIA_SET(receiver, TRANSCODE); MEDIA_SET(receiver, TRANSCODE);
__make_transcoder(handler, pt, pref_dest_codec); __make_transcoder(handler, pt, pref_dest_codec);
next:
l = l->next;
}
// if we've determined that we transcode, we must remove all unsupported codecs from
// the list, as we must expect to potentially receive media in that codec, which we
// then could not transcode.
if (MEDIA_ISSET(receiver, TRANSCODE)) {
for (GList *l = receiver->codecs_prefs_recv.head; l; ) {
struct rtp_payload_type *pt = l->data;
if (pt->codec_def) {
// supported
l = l->next;
continue;
}
ilog(LOG_DEBUG, "Stripping unsupported codec " STR_FORMAT " due to active transcoding",
STR_FMT(&pt->encoding));
l = __delete_receiver_codec(receiver, l);
}
} }
} }
@ -312,7 +417,7 @@ static struct rtp_payload_type *codec_add_payload_type(const str *codec, struct
if (pt->payload_type < 0) if (pt->payload_type < 0)
pt->payload_type = 96; // default first dynamic payload type number pt->payload_type = 96; // default first dynamic payload type number
while (1) { while (1) {
if (!g_hash_table_lookup(media->codecs, &pt->payload_type))
if (!g_hash_table_lookup(media->codecs_recv, &pt->payload_type))
break; // OK break; // OK
pt->payload_type++; pt->payload_type++;
if (pt->payload_type < 96) // if an RFC type was taken already if (pt->payload_type < 96) // if an RFC type was taken already
@ -343,44 +448,35 @@ static void __rtp_payload_type_dup(struct call *call, struct rtp_payload_type *p
call_str_cpy(call, &pt->encoding_parameters, &pt->encoding_parameters); call_str_cpy(call, &pt->encoding_parameters, &pt->encoding_parameters);
call_str_cpy(call, &pt->format_parameters, &pt->format_parameters); call_str_cpy(call, &pt->format_parameters, &pt->format_parameters);
} }
static struct rtp_payload_type *__rtp_payload_type_copy(struct rtp_payload_type *pt) {
struct rtp_payload_type *pt_copy = g_slice_alloc(sizeof(*pt));
*pt_copy = *pt;
return pt_copy;
}
static void __rtp_payload_type_add_name(GHashTable *ht, struct rtp_payload_type *pt)
{
GQueue *q = g_hash_table_lookup_queue_new(ht, &pt->encoding);
g_queue_push_tail(q, GUINT_TO_POINTER(pt->payload_type));
}
// consumes 'pt' // consumes 'pt'
static struct rtp_payload_type *__rtp_payload_type_add_recv(struct call_media *media,
static void __rtp_payload_type_add_recv(struct call_media *media,
struct rtp_payload_type *pt) struct rtp_payload_type *pt)
{ {
struct rtp_payload_type *existing_pt;
if ((existing_pt = g_hash_table_lookup(media->codecs, &pt->payload_type))) {
// collision/duplicate - ignore
payload_type_free(pt);
return existing_pt;
}
g_hash_table_replace(media->codecs, &pt->payload_type, pt);
GQueue *q = g_hash_table_lookup_queue_new(media->codec_names, &pt->encoding);
g_queue_push_tail(q, GUINT_TO_POINTER(pt->payload_type));
g_hash_table_insert(media->codecs_recv, &pt->payload_type, pt);
__rtp_payload_type_add_name(media->codec_names_recv, pt);
g_queue_push_tail(&media->codecs_prefs_recv, pt); g_queue_push_tail(&media->codecs_prefs_recv, pt);
return pt;
} }
// duplicates 'pt' // duplicates 'pt'
static void __rtp_payload_type_add_send(struct call_media *other_media, struct rtp_payload_type *pt) { static void __rtp_payload_type_add_send(struct call_media *other_media, struct rtp_payload_type *pt) {
// for the other side, we need a new 'pt' struct
struct rtp_payload_type *pt_copy = g_slice_alloc(sizeof(*pt));
*pt_copy = *pt;
g_queue_push_tail(&other_media->codecs_prefs_send, pt_copy);
// make sure we have at least an empty queue here to indicate support for this code.
// don't add anything to the queue as we don't know the reverse RTP payload type.
g_hash_table_lookup_queue_new(other_media->codec_names, &pt->encoding);
pt = __rtp_payload_type_copy(pt);
__rtp_payload_type_add_name(other_media->codec_names_send, pt);
g_queue_push_tail(&other_media->codecs_prefs_send, pt);
} }
// consumes 'pt' // consumes 'pt'
static void __rtp_payload_type_add(struct call_media *media, struct call_media *other_media, static void __rtp_payload_type_add(struct call_media *media, struct call_media *other_media,
struct rtp_payload_type *pt) struct rtp_payload_type *pt)
{ {
// if this payload type is already present in the 'codec' table, the _recv
// function frees its argument and returns the existing entry instead.
// otherwise it returns its argument.
pt = __rtp_payload_type_add_recv(media, pt);
__rtp_payload_type_add_recv(media, pt);
__rtp_payload_type_add_send(other_media, pt); __rtp_payload_type_add_send(other_media, pt);
} }
@ -415,10 +511,13 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_
int remove_all = 0; int remove_all = 0;
// start fresh // start fresh
g_queue_clear(&media->codecs_prefs_recv);
// receiving part for 'media'
g_queue_clear_full(&media->codecs_prefs_recv, (GDestroyNotify) payload_type_free);
g_hash_table_remove_all(media->codecs_recv);
g_hash_table_remove_all(media->codec_names_recv);
// and sending part for 'other_media'
g_queue_clear_full(&other_media->codecs_prefs_send, (GDestroyNotify) payload_type_free); g_queue_clear_full(&other_media->codecs_prefs_send, (GDestroyNotify) payload_type_free);
g_hash_table_remove_all(media->codecs);
g_hash_table_remove_all(media->codec_names);
g_hash_table_remove_all(other_media->codec_names_send);
if (strip && g_hash_table_lookup(strip, &str_all)) if (strip && g_hash_table_lookup(strip, &str_all))
remove_all = 1; remove_all = 1;
@ -454,7 +553,7 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_
if (__revert_codec_strip(removed, codec, media, other_media)) if (__revert_codec_strip(removed, codec, media, other_media))
continue; continue;
// also check if maybe the codec was never stripped // also check if maybe the codec was never stripped
if (g_hash_table_lookup(media->codec_names, codec)) {
if (g_hash_table_lookup(media->codec_names_recv, codec)) {
ilog(LOG_DEBUG, "Codec '" STR_FORMAT "' requested for transcoding is already present", ilog(LOG_DEBUG, "Codec '" STR_FORMAT "' requested for transcoding is already present",
STR_FMT(codec)); STR_FMT(codec));
continue; continue;


+ 1
- 1
daemon/recording.c View File

@ -713,7 +713,7 @@ static void setup_media_proc(struct call_media *media) {
if (!recording) if (!recording)
return; return;
GList *pltypes = g_hash_table_get_values(media->codecs);
GList *pltypes = g_hash_table_get_values(media->codecs_recv);
for (GList *l = pltypes; l; l = l->next) { for (GList *l = pltypes; l; l = l->next) {
struct rtp_payload_type *pt = l->data; struct rtp_payload_type *pt = l->data;


+ 4
- 4
daemon/redis.c View File

@ -1227,7 +1227,7 @@ static int rbl_cb_plts(str *s, GQueue *q, struct redis_list *list, void *ptr) {
pt->clock_rate = str_to_ui(&clock, 0); pt->clock_rate = str_to_ui(&clock, 0);
call_str_cpy(call, &pt->encoding_parameters, &enc_parms); call_str_cpy(call, &pt->encoding_parameters, &enc_parms);
call_str_cpy(call, &pt->format_parameters, &fmt_parms); call_str_cpy(call, &pt->format_parameters, &fmt_parms);
g_hash_table_replace(med->codecs, &pt->payload_type, pt);
g_hash_table_replace(med->codecs_recv, &pt->payload_type, pt);
return 0; return 0;
} }
static int json_medias(struct call *c, struct redis_list *medias, JsonReader *root_reader) { static int json_medias(struct call *c, struct redis_list *medias, JsonReader *root_reader) {
@ -1242,7 +1242,7 @@ static int json_medias(struct call *c, struct redis_list *medias, JsonReader *ro
/* from call.c:__get_media() */ /* from call.c:__get_media() */
med = uid_slice_alloc0(med, &c->medias); med = uid_slice_alloc0(med, &c->medias);
med->call = c; med->call = c;
med->codecs = g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
med->codecs_recv = g_hash_table_new_full(g_int_hash, g_int_equal, NULL,
(GDestroyNotify) payload_type_free); (GDestroyNotify) payload_type_free);
if (redis_hash_get_unsigned(&med->index, rh, "index")) if (redis_hash_get_unsigned(&med->index, rh, "index"))
@ -1387,7 +1387,7 @@ static int json_link_streams(struct call *c, struct redis_list *streams,
return -1; return -1;
if (ps->media) if (ps->media)
__rtp_stats_update(ps->rtp_stats, ps->media->codecs);
__rtp_stats_update(ps->rtp_stats, ps->media->codecs_recv);
} }
return 0; return 0;
@ -2009,7 +2009,7 @@ char* redis_encode_json(struct call *c) {
} }
json_builder_end_array (builder); json_builder_end_array (builder);
k = g_hash_table_get_values(media->codecs);
k = g_hash_table_get_values(media->codecs_recv);
snprintf(tmp, sizeof(tmp), "payload_types-%u", media->unique_id); snprintf(tmp, sizeof(tmp), "payload_types-%u", media->unique_id);
json_builder_set_member_name(builder, tmp); json_builder_set_member_name(builder, tmp);
json_builder_begin_array (builder); json_builder_begin_array (builder);


+ 1
- 1
daemon/ssrc.c View File

@ -292,7 +292,7 @@ void ssrc_receiver_report(struct call_media *m, const struct ssrc_receiver_repor
} }
} }
const struct rtp_payload_type *rpt = rtp_payload_type(pt, m->codecs);
const struct rtp_payload_type *rpt = rtp_payload_type(pt, m->codecs_recv);
if (!rpt) { if (!rpt) {
ilog(LOG_INFO, "Invalid RTP payload type %i, discarding RTCP RR", pt); ilog(LOG_INFO, "Invalid RTP payload type %i, discarding RTCP RR", pt);
goto out_nl; goto out_nl;


Loading…
Cancel
Save