|
|
|
@ -100,6 +100,12 @@ struct transcode_packet { |
|
|
|
int (*dup_func)(struct codec_ssrc_handler *, struct transcode_packet *, struct media_packet *); |
|
|
|
struct rtp_header rtp; |
|
|
|
}; |
|
|
|
struct supp_codec_tracker { |
|
|
|
GHashTable *clockrates; // 8000, 16000, etc, for each real audio codec that is present |
|
|
|
GHashTable *touched; // 8000, 16000, etc, for each audio codec that was touched (added, removed, etc) |
|
|
|
int all_touched; |
|
|
|
GHashTable *supp_codecs; // telephone-event etc => hash table of clock rates |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
static codec_handler_func handler_func_passthrough_ssrc; |
|
|
|
@ -624,7 +630,7 @@ static void __symmetric_codecs(struct call_media *receiver, struct call_media *s |
|
|
|
// add it to the list |
|
|
|
ilog(LOG_DEBUG, "Adding symmetric RTP payload type %i", pt->payload_type); |
|
|
|
g_hash_table_steal(prefs_recv, GINT_TO_POINTER(pt->payload_type)); |
|
|
|
__rtp_payload_type_add_recv(receiver, out_pt, 1); |
|
|
|
__rtp_payload_type_add_recv(receiver, out_pt, 1, NULL); |
|
|
|
// and our send leg |
|
|
|
out_pt = g_hash_table_lookup(prefs_send, GINT_TO_POINTER(pt->payload_type)); |
|
|
|
if (out_pt) { |
|
|
|
@ -649,7 +655,7 @@ static void __symmetric_codecs(struct call_media *receiver, struct call_media *s |
|
|
|
if (!out_pt) |
|
|
|
continue; |
|
|
|
g_hash_table_steal(prefs_recv, ptype); |
|
|
|
__rtp_payload_type_add_recv(receiver, out_pt, 1); |
|
|
|
__rtp_payload_type_add_recv(receiver, out_pt, 1, NULL); |
|
|
|
} |
|
|
|
while (prefs_send_order.length) { |
|
|
|
void *ptype = g_queue_pop_head(&prefs_send_order); |
|
|
|
@ -2155,36 +2161,6 @@ static struct rtp_payload_type *codec_add_payload_type(const str *codec, struct |
|
|
|
return pt; |
|
|
|
} |
|
|
|
|
|
|
|
// handle special meaning "clock rate == 1": add one instance of this PT for each clock rate |
|
|
|
// that is already present |
|
|
|
static int __codec_synth_transcode_options(struct rtp_payload_type *pt, struct sdp_ng_flags *flags, |
|
|
|
struct call_media *media) |
|
|
|
{ |
|
|
|
if (pt->clock_rate != 1) |
|
|
|
return 0; |
|
|
|
|
|
|
|
GHashTable *clockrates = g_hash_table_new(g_direct_hash, g_direct_equal); |
|
|
|
|
|
|
|
// special handling - add one instance for each clock rate that is present |
|
|
|
for (GList *k = media->codecs_prefs_recv.head; k; k = k->next) { |
|
|
|
struct rtp_payload_type *pt_r = k->data; |
|
|
|
if (g_hash_table_lookup(clockrates, GUINT_TO_POINTER(pt_r->clock_rate))) |
|
|
|
continue; |
|
|
|
char *pt_s = g_strdup_printf(STR_FORMAT "/%u", STR_FMT(&pt->encoding), pt_r->clock_rate); |
|
|
|
g_hash_table_insert(clockrates, GUINT_TO_POINTER(pt_r->clock_rate), (void *) 1); |
|
|
|
str pt_str; |
|
|
|
str_init(&pt_str, pt_s); |
|
|
|
ilog(LOG_DEBUG, "Synthesised transcoding option for '%s'", pt_s); |
|
|
|
g_queue_push_tail(&flags->codec_transcode, str_dup(&pt_str)); |
|
|
|
g_free(pt_s); |
|
|
|
} |
|
|
|
|
|
|
|
payload_type_free(pt); |
|
|
|
g_hash_table_destroy(clockrates); |
|
|
|
|
|
|
|
return 1; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
@ -2212,17 +2188,21 @@ static void __rtp_payload_type_add_name(GHashTable *ht, struct rtp_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)); |
|
|
|
} |
|
|
|
static void __queue_insert_supp(GQueue *q, struct rtp_payload_type *pt, int supp_check) { |
|
|
|
static void __queue_insert_supp(GQueue *q, struct rtp_payload_type *pt, int supp_check, |
|
|
|
struct supp_codec_tracker *sct) |
|
|
|
{ |
|
|
|
int is_supp = pt->codec_def && pt->codec_def->supplemental; |
|
|
|
|
|
|
|
// do we care at all? |
|
|
|
if (!supp_check) { |
|
|
|
g_queue_push_tail(q, pt); |
|
|
|
return; |
|
|
|
goto do_sct; |
|
|
|
} |
|
|
|
|
|
|
|
// all new supp codecs go last |
|
|
|
if (pt->codec_def && pt->codec_def->supplemental) { |
|
|
|
if (is_supp) { |
|
|
|
g_queue_push_tail(q, pt); |
|
|
|
return; |
|
|
|
goto do_sct; |
|
|
|
} |
|
|
|
|
|
|
|
// find the cut-off point between non-supp and supp codecs |
|
|
|
@ -2235,15 +2215,31 @@ static void __queue_insert_supp(GQueue *q, struct rtp_payload_type *pt, int supp |
|
|
|
} |
|
|
|
} |
|
|
|
// do we have any non-supp codecs? |
|
|
|
if (!insert_pos) { |
|
|
|
if (!insert_pos) |
|
|
|
g_queue_push_head(q, pt); |
|
|
|
else |
|
|
|
g_queue_insert_after(q, insert_pos, pt); |
|
|
|
|
|
|
|
do_sct: |
|
|
|
if (!sct) |
|
|
|
return; |
|
|
|
if (!is_supp) |
|
|
|
g_hash_table_replace(sct->clockrates, GUINT_TO_POINTER(pt->clock_rate), (void *) 0x1); |
|
|
|
else { |
|
|
|
GHashTable *clockrates = g_hash_table_lookup(sct->supp_codecs, &pt->encoding); |
|
|
|
if (!clockrates) { |
|
|
|
clockrates = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, |
|
|
|
(GDestroyNotify) g_queue_free); |
|
|
|
g_hash_table_replace(sct->supp_codecs, &pt->encoding, clockrates); |
|
|
|
} |
|
|
|
GQueue *entries = g_hash_table_lookup_queue_new(clockrates, GUINT_TO_POINTER(pt->clock_rate)); |
|
|
|
// new supp entries are always last |
|
|
|
g_queue_push_tail(entries, q->tail); |
|
|
|
} |
|
|
|
g_queue_insert_after(q, insert_pos, pt); |
|
|
|
} |
|
|
|
// consumes 'pt' |
|
|
|
void __rtp_payload_type_add_recv(struct call_media *media, |
|
|
|
struct rtp_payload_type *pt, int supp_check) |
|
|
|
struct rtp_payload_type *pt, int supp_check, struct supp_codec_tracker *sct) |
|
|
|
{ |
|
|
|
if (!pt) |
|
|
|
return; |
|
|
|
@ -2257,7 +2253,7 @@ void __rtp_payload_type_add_recv(struct call_media *media, |
|
|
|
pt->ptime = media->ptime; |
|
|
|
g_hash_table_insert(media->codecs_recv, &pt->payload_type, pt); |
|
|
|
__rtp_payload_type_add_name(media->codec_names_recv, pt); |
|
|
|
__queue_insert_supp(&media->codecs_prefs_recv, pt, supp_check); |
|
|
|
__queue_insert_supp(&media->codecs_prefs_recv, pt, supp_check, sct); |
|
|
|
} |
|
|
|
// consumes 'pt' |
|
|
|
void __rtp_payload_type_add_send(struct call_media *other_media, |
|
|
|
@ -2287,10 +2283,10 @@ void __rtp_payload_type_add_send_dup(struct call_media *other_media, |
|
|
|
} |
|
|
|
// consumes 'pt' |
|
|
|
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, struct supp_codec_tracker *sct) |
|
|
|
{ |
|
|
|
__rtp_payload_type_add_send_dup(other_media, pt); |
|
|
|
__rtp_payload_type_add_recv(media, pt, 0); |
|
|
|
__rtp_payload_type_add_recv(media, pt, 0, sct); |
|
|
|
} |
|
|
|
|
|
|
|
static void __payload_queue_free(void *qq) { |
|
|
|
@ -2298,7 +2294,7 @@ 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, struct supp_codec_tracker *sct) |
|
|
|
{ |
|
|
|
GQueue *q = g_hash_table_lookup(removed, codec); |
|
|
|
if (!q) |
|
|
|
@ -2308,7 +2304,7 @@ static int __revert_codec_strip(GHashTable *removed, const str *codec, |
|
|
|
g_hash_table_steal(removed, codec); |
|
|
|
for (GList *l = q->head; l; l = l->next) { |
|
|
|
struct rtp_payload_type *pt = l->data; |
|
|
|
__rtp_payload_type_add(media, other_media, pt); |
|
|
|
__rtp_payload_type_add(media, other_media, pt, sct); |
|
|
|
} |
|
|
|
g_queue_free(q); |
|
|
|
return 1; |
|
|
|
@ -2343,6 +2339,103 @@ static void __codec_options_set(struct call *call, struct rtp_payload_type *pt, |
|
|
|
if (__codec_options_set1(call, pt, &pt->encoding, codec_set)) |
|
|
|
return; |
|
|
|
} |
|
|
|
static void supp_codec_tracker_init(struct supp_codec_tracker *sct) { |
|
|
|
ZERO(*sct); |
|
|
|
sct->clockrates = g_hash_table_new(g_direct_hash, g_direct_equal); |
|
|
|
sct->touched = g_hash_table_new(g_direct_hash, g_direct_equal); |
|
|
|
sct->supp_codecs = g_hash_table_new_full(str_case_hash, str_case_equal, NULL, |
|
|
|
(GDestroyNotify) g_hash_table_destroy); |
|
|
|
} |
|
|
|
static void supp_codec_tracker_destroy(struct supp_codec_tracker *sct) { |
|
|
|
g_hash_table_destroy(sct->clockrates); |
|
|
|
g_hash_table_destroy(sct->touched); |
|
|
|
g_hash_table_destroy(sct->supp_codecs); |
|
|
|
} |
|
|
|
static void codec_touched(struct rtp_payload_type *pt, struct supp_codec_tracker *sct, struct call_media *media) { |
|
|
|
ensure_codec_def(pt, media); |
|
|
|
if (pt->codec_def && pt->codec_def->supplemental) { |
|
|
|
sct->all_touched = 1; |
|
|
|
return; |
|
|
|
} |
|
|
|
g_hash_table_replace(sct->touched, GUINT_TO_POINTER(pt->clock_rate), (void *) 0x1); |
|
|
|
} |
|
|
|
static int ptr_cmp(const void *a, const void *b) { |
|
|
|
if (a < b) |
|
|
|
return -1; |
|
|
|
if (a > b) |
|
|
|
return 1; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
static void supp_codecs_fixup(struct supp_codec_tracker *sct, struct call_media *media) { |
|
|
|
// get all supported audio cloc krates |
|
|
|
GList *clockrates = g_hash_table_get_keys(sct->clockrates); |
|
|
|
// and to ensure consistent results |
|
|
|
clockrates = g_list_sort(clockrates, ptr_cmp); |
|
|
|
|
|
|
|
// for each supplemental codec supported ... |
|
|
|
GList *supp_codecs = g_hash_table_get_keys(sct->supp_codecs); |
|
|
|
|
|
|
|
for (GList *l = supp_codecs; l; l = l->next) { |
|
|
|
// ... compare the list of clock rates against the clock rates supported by the audio codecs |
|
|
|
str *supp_codec = l->data; |
|
|
|
GHashTable *supp_clockrates = g_hash_table_lookup(sct->supp_codecs, supp_codec); |
|
|
|
|
|
|
|
// iterate audio clock rates and check against supp clockrates |
|
|
|
for (GList *k = clockrates; k; k = k->next) { |
|
|
|
unsigned int clockrate = GPOINTER_TO_UINT(k->data); |
|
|
|
|
|
|
|
// is this already supported? |
|
|
|
if (g_hash_table_lookup(supp_clockrates, GUINT_TO_POINTER(clockrate))) { |
|
|
|
// good, remember this |
|
|
|
g_hash_table_remove(supp_clockrates, GUINT_TO_POINTER(clockrate)); |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
// ignore if we haven't touched anything with that clock rate |
|
|
|
if (!sct->all_touched && !g_hash_table_lookup(sct->touched, GUINT_TO_POINTER(clockrate))) |
|
|
|
continue; |
|
|
|
|
|
|
|
ilog(LOG_DEBUG, "Adding supplemental codec " STR_FORMAT " for clock rate %u", STR_FMT(supp_codec), clockrate); |
|
|
|
|
|
|
|
char *pt_s = g_strdup_printf(STR_FORMAT "/%u", STR_FMT(supp_codec), clockrate); |
|
|
|
str pt_str; |
|
|
|
str_init(&pt_str, pt_s); |
|
|
|
|
|
|
|
struct rtp_payload_type *pt = codec_add_payload_type(&pt_str, media); |
|
|
|
if (!pt) |
|
|
|
continue; |
|
|
|
pt->for_transcoding = 1; |
|
|
|
|
|
|
|
__rtp_payload_type_add_recv(media, pt, 1, NULL); |
|
|
|
|
|
|
|
g_free(pt_s); |
|
|
|
} |
|
|
|
|
|
|
|
// finally check which clock rates are left over and remove those |
|
|
|
GList *to_remove = g_hash_table_get_keys(supp_clockrates); |
|
|
|
for (GList *k = to_remove; k; k = k->next) { |
|
|
|
unsigned int clockrate = GPOINTER_TO_UINT(k->data); |
|
|
|
|
|
|
|
// ignore if we haven't touched anything with that clock rate |
|
|
|
if (!sct->all_touched && !g_hash_table_lookup(sct->touched, GUINT_TO_POINTER(clockrate))) |
|
|
|
continue; |
|
|
|
|
|
|
|
GQueue *entries = g_hash_table_lookup(supp_clockrates, GUINT_TO_POINTER(clockrate)); |
|
|
|
for (GList *j = entries->head; j; j = j->next) { |
|
|
|
GList *link = j->data; |
|
|
|
struct rtp_payload_type *pt = link->data; |
|
|
|
|
|
|
|
ilog(LOG_DEBUG, "Eliminating supplemental codec " STR_FORMAT " with stray clock rate %u", |
|
|
|
STR_FMT(&pt->encoding), clockrate); |
|
|
|
|
|
|
|
__delete_receiver_codec(media, link); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
g_list_free(supp_codecs); |
|
|
|
g_list_free(clockrates); |
|
|
|
} |
|
|
|
void codec_rtp_payload_types(struct call_media *media, struct call_media *other_media, |
|
|
|
GQueue *types, struct sdp_ng_flags *flags) |
|
|
|
{ |
|
|
|
@ -2380,6 +2473,9 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ |
|
|
|
if (flags->codec_mask && g_hash_table_lookup(flags->codec_mask, &str_all)) |
|
|
|
mask_all = 1; |
|
|
|
|
|
|
|
struct supp_codec_tracker sct; |
|
|
|
supp_codec_tracker_init(&sct); |
|
|
|
|
|
|
|
/* we steal the entire list to avoid duplicate allocs */ |
|
|
|
while ((pt = g_queue_pop_head(types))) { |
|
|
|
__rtp_payload_type_dup(call, pt); // this takes care of string allocation |
|
|
|
@ -2391,6 +2487,7 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ |
|
|
|
{ |
|
|
|
ilog(LOG_DEBUG, "Stripping codec '" STR_FORMAT "'", |
|
|
|
STR_FMT(&pt->encoding_with_params)); |
|
|
|
codec_touched(pt, &sct, media); |
|
|
|
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); |
|
|
|
@ -2401,15 +2498,17 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ |
|
|
|
__codec_options_set(call, pt, flags->codec_set); |
|
|
|
if (!mask_all && (!flags->codec_mask || !g_hash_table_lookup(flags->codec_mask, &pt->encoding)) |
|
|
|
&& (!flags->codec_mask || !g_hash_table_lookup(flags->codec_mask, &pt->encoding_with_params))) |
|
|
|
__rtp_payload_type_add(media, other_media, pt); |
|
|
|
else |
|
|
|
__rtp_payload_type_add(media, other_media, pt, &sct); |
|
|
|
else { |
|
|
|
codec_touched(pt, &sct, other_media); |
|
|
|
__rtp_payload_type_add_send(other_media, pt); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// now restore codecs that have been removed, but should be offered |
|
|
|
for (GList *l = flags->codec_offer.head; l; l = l->next) { |
|
|
|
str *codec = l->data; |
|
|
|
__revert_codec_strip(removed, codec, media, other_media); |
|
|
|
__revert_codec_strip(removed, codec, media, other_media, &sct); |
|
|
|
} |
|
|
|
|
|
|
|
if (!flags->asymmetric_codecs) { |
|
|
|
@ -2435,7 +2534,7 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ |
|
|
|
// and removed by a strip=all option, |
|
|
|
// simply restore it from the original list and handle it the same way |
|
|
|
// as 'offer' |
|
|
|
if (strip_all && __revert_codec_strip(removed, codec, media, other_media)) |
|
|
|
if (strip_all && __revert_codec_strip(removed, codec, media, other_media, &sct)) |
|
|
|
continue; |
|
|
|
// also check if maybe the codec was never stripped |
|
|
|
if (g_hash_table_lookup(media->codec_names_recv, codec)) { |
|
|
|
@ -2449,13 +2548,11 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ |
|
|
|
if (!pt) |
|
|
|
continue; |
|
|
|
pt->for_transcoding = 1; |
|
|
|
|
|
|
|
if (__codec_synth_transcode_options(pt, flags, media)) |
|
|
|
continue; |
|
|
|
codec_touched(pt, &sct, media); |
|
|
|
|
|
|
|
ilog(LOG_DEBUG, "Codec '" STR_FORMAT "' added for transcoding with payload type %u", |
|
|
|
STR_FMT(&pt->encoding_with_params), pt->payload_type); |
|
|
|
__rtp_payload_type_add_recv(media, pt, 1); |
|
|
|
__rtp_payload_type_add_recv(media, pt, 1, &sct); |
|
|
|
} |
|
|
|
|
|
|
|
if (media->type_id == MT_AUDIO && other_media->type_id == MT_IMAGE) { |
|
|
|
@ -2471,7 +2568,7 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ |
|
|
|
if (media->t38_gateway && media->t38_gateway->pcm_player |
|
|
|
&& media->t38_gateway->pcm_player->handler) |
|
|
|
__rtp_payload_type_add_recv(media, |
|
|
|
__rtp_payload_type_copy(&media->t38_gateway->pcm_player->handler->dest_pt), 1); |
|
|
|
__rtp_payload_type_copy(&media->t38_gateway->pcm_player->handler->dest_pt), 1, &sct); |
|
|
|
} |
|
|
|
else if (flags->opmode == OP_OFFER) { |
|
|
|
// T.38 -> audio transcoder, initial offer, and no codecs have been given. |
|
|
|
@ -2481,10 +2578,10 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ |
|
|
|
static const str PCMA_str = STR_CONST_INIT("PCMA"); |
|
|
|
pt = codec_add_payload_type(&PCMU_str, media); |
|
|
|
assert(pt != NULL); |
|
|
|
__rtp_payload_type_add_recv(media, pt, 1); |
|
|
|
__rtp_payload_type_add_recv(media, pt, 1, &sct); |
|
|
|
pt = codec_add_payload_type(&PCMA_str, media); |
|
|
|
assert(pt != NULL); |
|
|
|
__rtp_payload_type_add_recv(media, pt, 1); |
|
|
|
__rtp_payload_type_add_recv(media, pt, 1, &sct); |
|
|
|
|
|
|
|
ilog(LOG_DEBUG, "Using default codecs PCMU and PCMA for T.38 gateway"); |
|
|
|
} |
|
|
|
@ -2509,5 +2606,8 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
supp_codecs_fixup(&sct, media); |
|
|
|
|
|
|
|
g_hash_table_destroy(removed); |
|
|
|
supp_codec_tracker_destroy(&sct); |
|
|
|
} |