|
|
|
@ -89,8 +89,8 @@ static void __make_transcoder(struct codec_handler *handler, struct rtp_payload_ |
|
|
|
goto reset; |
|
|
|
|
|
|
|
ilog(LOG_DEBUG, "Leaving transcode context for " STR_FORMAT "/%u/%i -> " STR_FORMAT "/%u/%i intact", |
|
|
|
STR_FMT(&source->encoding), source->clock_rate, source->channels, |
|
|
|
STR_FMT(&dest->encoding), dest->clock_rate, dest->channels); |
|
|
|
STR_FMT(&source->encoding_with_params), source->clock_rate, source->channels, |
|
|
|
STR_FMT(&dest->encoding_with_params), dest->clock_rate, dest->channels); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
@ -105,8 +105,8 @@ reset: |
|
|
|
handler); |
|
|
|
|
|
|
|
ilog(LOG_DEBUG, "Created transcode context for " STR_FORMAT "/%u/%i -> " STR_FORMAT "/%u/%i", |
|
|
|
STR_FMT(&source->encoding), source->clock_rate, source->channels, |
|
|
|
STR_FMT(&dest->encoding), dest->clock_rate, dest->channels); |
|
|
|
STR_FMT(&source->encoding_with_params), source->clock_rate, source->channels, |
|
|
|
STR_FMT(&dest->encoding_with_params), dest->clock_rate, dest->channels); |
|
|
|
} |
|
|
|
|
|
|
|
static void __ensure_codec_def(struct rtp_payload_type *pt, struct call_media *media) { |
|
|
|
@ -124,6 +124,7 @@ static GList *__delete_receiver_codec(struct call_media *receiver, GList *link) |
|
|
|
|
|
|
|
g_hash_table_remove(receiver->codecs_recv, &pt->payload_type); |
|
|
|
g_hash_table_remove(receiver->codec_names_recv, &pt->encoding); |
|
|
|
g_hash_table_remove(receiver->codec_names_recv, &pt->encoding_with_params); |
|
|
|
|
|
|
|
GList *next = link->next; |
|
|
|
g_queue_delete_link(&receiver->codecs_prefs_recv, link); |
|
|
|
@ -159,7 +160,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink) |
|
|
|
pt->ptime = sink->ptime; |
|
|
|
|
|
|
|
if (!pref_dest_codec) { |
|
|
|
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_with_params)); |
|
|
|
pref_dest_codec = pt; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -194,7 +195,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink) |
|
|
|
|
|
|
|
if (pt->codec_def->avcodec_id != -1) { |
|
|
|
ilog(LOG_DEBUG, "Accepting offered codec " STR_FORMAT " due to transcoding", |
|
|
|
STR_FMT(&pt->encoding)); |
|
|
|
STR_FMT(&pt->encoding_with_params)); |
|
|
|
MEDIA_SET(receiver, TRANSCODE); |
|
|
|
} |
|
|
|
|
|
|
|
@ -224,7 +225,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink) |
|
|
|
// 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)); |
|
|
|
STR_FMT(&pt->encoding_with_params)); |
|
|
|
|
|
|
|
l = __delete_receiver_codec(receiver, l); |
|
|
|
continue; |
|
|
|
@ -235,7 +236,8 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink) |
|
|
|
struct codec_handler *handler; |
|
|
|
handler = g_hash_table_lookup(receiver->codec_handlers, &pt->payload_type); |
|
|
|
if (!handler) { |
|
|
|
ilog(LOG_DEBUG, "Creating codec handler for " STR_FORMAT, STR_FMT(&pt->encoding)); |
|
|
|
ilog(LOG_DEBUG, "Creating codec handler for " STR_FORMAT, |
|
|
|
STR_FMT(&pt->encoding_with_params)); |
|
|
|
handler = __handler_new(pt); |
|
|
|
g_hash_table_insert(receiver->codec_handlers, &handler->source_pt.payload_type, |
|
|
|
handler); |
|
|
|
@ -261,7 +263,8 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink) |
|
|
|
// to do. most likely this is an initial offer without a received answer. |
|
|
|
// we default to forwarding without transcoding. |
|
|
|
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_with_params)); |
|
|
|
__make_passthrough(handler); |
|
|
|
goto next; |
|
|
|
} |
|
|
|
@ -297,14 +300,14 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink) |
|
|
|
} |
|
|
|
|
|
|
|
// 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_with_params)); |
|
|
|
__make_passthrough(handler); |
|
|
|
goto next; |
|
|
|
} |
|
|
|
|
|
|
|
unsupported: |
|
|
|
// 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_with_params)); |
|
|
|
dest_pt = pref_dest_codec; |
|
|
|
transcode: |
|
|
|
MEDIA_SET(receiver, TRANSCODE); |
|
|
|
@ -410,9 +413,8 @@ static struct ssrc_entry *__ssrc_handler_new(void *p) { |
|
|
|
ch->encoder = encoder_new(); |
|
|
|
if (!ch->encoder) |
|
|
|
goto err; |
|
|
|
// XXX make bitrate configurable |
|
|
|
if (encoder_config(ch->encoder, h->dest_pt.codec_def, |
|
|
|
h->dest_pt.codec_def->default_bitrate, |
|
|
|
h->dest_pt.bitrate ? : h->dest_pt.codec_def->default_bitrate, |
|
|
|
ch->ptime, |
|
|
|
&enc_format, &ch->encoder_format)) |
|
|
|
goto err; |
|
|
|
@ -592,15 +594,22 @@ void codec_packet_free(void *pp) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct rtp_payload_type *codec_make_dynamic_payload_type(const codec_def_t *dec, struct call *call) { |
|
|
|
static struct rtp_payload_type *codec_make_dynamic_payload_type(const codec_def_t *dec, struct call_media *media, |
|
|
|
int clockrate, int channels, int bitrate) |
|
|
|
{ |
|
|
|
if (dec->default_channels <= 0 || dec->default_clockrate < 0) |
|
|
|
return NULL; |
|
|
|
|
|
|
|
struct rtp_payload_type *ret = g_slice_alloc0(sizeof(*ret)); |
|
|
|
ret->payload_type = -1; |
|
|
|
str_init(&ret->encoding, (char *) dec->rtpname); |
|
|
|
ret->clock_rate = dec->default_clockrate; |
|
|
|
ret->channels = dec->default_channels; |
|
|
|
ret->clock_rate = clockrate ? : dec->default_clockrate; |
|
|
|
ret->channels = channels ? : dec->default_channels; |
|
|
|
ret->bitrate = bitrate; |
|
|
|
ret->ptime = media->ptime ? : dec->default_ptime; |
|
|
|
|
|
|
|
if (dec->init) |
|
|
|
dec->init(ret); |
|
|
|
|
|
|
|
char full_encoding[64]; |
|
|
|
char params[32] = ""; |
|
|
|
@ -618,15 +627,29 @@ static struct rtp_payload_type *codec_make_dynamic_payload_type(const codec_def_ |
|
|
|
ret->format_parameters = STR_EMPTY; |
|
|
|
ret->codec_def = dec; |
|
|
|
|
|
|
|
__rtp_payload_type_dup(call, ret); |
|
|
|
__rtp_payload_type_dup(media->call, ret); |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// XXX allow specifying codec params (e.g. "transcode=opus/16000/1") |
|
|
|
static struct rtp_payload_type *codec_make_payload_type(const str *codec, struct call_media *media) { |
|
|
|
struct call *call = media->call; |
|
|
|
const codec_def_t *dec = codec_find(codec, media->type_id); |
|
|
|
static struct rtp_payload_type *codec_make_payload_type(const str *codec_str, struct call_media *media) { |
|
|
|
str codec_fmt = *codec_str; |
|
|
|
str codec, parms, chans, opts; |
|
|
|
if (str_token_sep(&codec, &codec_fmt, '/')) |
|
|
|
return NULL; |
|
|
|
str_token_sep(&parms, &codec_fmt, '/'); |
|
|
|
str_token_sep(&chans, &codec_fmt, '/'); |
|
|
|
str_token_sep(&opts, &codec_fmt, '/'); |
|
|
|
|
|
|
|
int clockrate = str_to_i(&parms, 0); |
|
|
|
int channels = str_to_i(&chans, 0); |
|
|
|
int bitrate = str_to_i(&opts, 0); |
|
|
|
|
|
|
|
if (clockrate && !channels) |
|
|
|
channels = 1; |
|
|
|
|
|
|
|
const codec_def_t *dec = codec_find(&codec, media->type_id); |
|
|
|
if (!dec) |
|
|
|
return NULL; |
|
|
|
// we must support both encoding and decoding |
|
|
|
@ -637,13 +660,17 @@ static struct rtp_payload_type *codec_make_payload_type(const str *codec, struct |
|
|
|
|
|
|
|
if (dec->rfc_payload_type >= 0) { |
|
|
|
const struct rtp_payload_type *rfc_pt = rtp_get_rfc_payload_type(dec->rfc_payload_type); |
|
|
|
if (rfc_pt) { |
|
|
|
// only use the RFC payload type if all parameters match |
|
|
|
if (rfc_pt |
|
|
|
&& (clockrate == 0 || clockrate == rfc_pt->clock_rate) |
|
|
|
&& (channels == 0 || channels == rfc_pt->channels)) |
|
|
|
{ |
|
|
|
struct rtp_payload_type *ret = __rtp_payload_type_copy(rfc_pt); |
|
|
|
ret->codec_def = dec; |
|
|
|
return ret; |
|
|
|
} |
|
|
|
} |
|
|
|
return codec_make_dynamic_payload_type(dec, call); |
|
|
|
return codec_make_dynamic_payload_type(dec, media, clockrate, channels, bitrate); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
@ -667,7 +694,7 @@ static struct rtp_payload_type *codec_add_payload_type(const str *codec, struct |
|
|
|
else if (pt->payload_type >= 128) { |
|
|
|
ilog(LOG_WARN, "Ran out of RTP payload type numbers while adding codec '" |
|
|
|
STR_FORMAT "' for transcoding", |
|
|
|
STR_FMT(codec)); |
|
|
|
STR_FMT(&pt->encoding_with_params)); |
|
|
|
payload_type_free(pt); |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
@ -699,6 +726,8 @@ static void __rtp_payload_type_add_name(GHashTable *ht, struct rtp_payload_type |
|
|
|
{ |
|
|
|
GQueue *q = g_hash_table_lookup_queue_new(ht, &pt->encoding); |
|
|
|
g_queue_push_tail(q, GUINT_TO_POINTER(pt->payload_type)); |
|
|
|
q = g_hash_table_lookup_queue_new(ht, &pt->encoding_with_params); |
|
|
|
g_queue_push_tail(q, GUINT_TO_POINTER(pt->payload_type)); |
|
|
|
} |
|
|
|
// consumes 'pt' |
|
|
|
static void __rtp_payload_type_add_recv(struct call_media *media, |
|
|
|
@ -709,7 +738,9 @@ static void __rtp_payload_type_add_recv(struct call_media *media, |
|
|
|
g_queue_push_tail(&media->codecs_prefs_recv, 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) |
|
|
|
{ |
|
|
|
pt = __rtp_payload_type_copy(pt); |
|
|
|
g_hash_table_insert(other_media->codecs_send, &pt->payload_type, pt); |
|
|
|
__rtp_payload_type_add_name(other_media->codec_names_send, pt); |
|
|
|
@ -728,7 +759,8 @@ static void __payload_queue_free(void *qq) { |
|
|
|
g_queue_free_full(q, (GDestroyNotify) payload_type_free); |
|
|
|
} |
|
|
|
static int __revert_codec_strip(GHashTable *removed, const str *codec, |
|
|
|
struct call_media *media, struct call_media *other_media) { |
|
|
|
struct call_media *media, struct call_media *other_media) |
|
|
|
{ |
|
|
|
GQueue *q = g_hash_table_lookup(removed, codec); |
|
|
|
if (!q) |
|
|
|
return 0; |
|
|
|
@ -772,9 +804,14 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ |
|
|
|
|
|
|
|
// codec stripping |
|
|
|
if (strip) { |
|
|
|
if (remove_all || g_hash_table_lookup(strip, &pt->encoding)) { |
|
|
|
ilog(LOG_DEBUG, "Stripping codec '" STR_FORMAT "'", STR_FMT(&pt->encoding)); |
|
|
|
if (remove_all || g_hash_table_lookup(strip, &pt->encoding) |
|
|
|
|| g_hash_table_lookup(strip, &pt->encoding_with_params)) |
|
|
|
{ |
|
|
|
ilog(LOG_DEBUG, "Stripping codec '" STR_FORMAT "'", |
|
|
|
STR_FMT(&pt->encoding_with_params)); |
|
|
|
GQueue *q = g_hash_table_lookup_queue_new(removed, &pt->encoding); |
|
|
|
g_queue_push_tail(q, __rtp_payload_type_copy(pt)); |
|
|
|
q = g_hash_table_lookup_queue_new(removed, &pt->encoding_with_params); |
|
|
|
g_queue_push_tail(q, pt); |
|
|
|
continue; |
|
|
|
} |
|
|
|
@ -809,7 +846,7 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ |
|
|
|
continue; |
|
|
|
|
|
|
|
ilog(LOG_DEBUG, "Codec '" STR_FORMAT "' added for transcoding with payload type %u", |
|
|
|
STR_FMT(codec), pt->payload_type); |
|
|
|
STR_FMT(&pt->encoding_with_params), pt->payload_type); |
|
|
|
__rtp_payload_type_add_recv(media, pt); |
|
|
|
} |
|
|
|
|
|
|
|
|