From 336cb5b280079b0e7b24127ef987e10293604490 Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Fri, 20 Nov 2020 13:53:30 -0500 Subject: [PATCH] TT#101653 add codec-consume and codec-accept options Change-Id: Icac343b34881628118f2c36181be66db1c6286e0 --- README.md | 13 +++ daemon/call_interfaces.c | 12 ++ daemon/codec.c | 234 +++++++++++++++++++++++++++++++++----- include/call_interfaces.h | 2 + t/auto-daemon-tests.pl | 89 ++++++++++++++- t/transcode-test.c | 2 + utils/rtpengine-ng-client | 4 +- 7 files changed, 323 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index e611cc815..cd5993eec 100644 --- a/README.md +++ b/README.md @@ -1234,6 +1234,19 @@ Optionally included keys are: This option is only processed in `offer` messages and ignored otherwise. + * `consume` + + Identical to `mask` but enables the transcoding engine even if no other transcoding + related options are given. + + * `accept` + + Similar to `mask` and `consume` but doesn't remove the codec from the list of + offered codecs. This means that a codec listed under `accept` will still be offered + to the remote peer, but if the remote peer rejects it, it will still be accepted + torwards the original offerer and then used for transcoding. It is a more selective + version of what the `always transcode` flag does. + * `set` Contains a list of strings. This list makes it possible to set codec options diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 5d663968a..d7eea712c 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -865,6 +865,12 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) { if (call_ng_flags_prefix(out, s, "codec-set-", call_ng_flags_str_ht_split, &out->codec_set)) return; + if (call_ng_flags_prefix(out, s, "codec-accept-", call_ng_flags_str_ht, + &out->codec_accept)) + return; + if (call_ng_flags_prefix(out, s, "codec-consume-", call_ng_flags_str_ht, + &out->codec_consume)) + return; if (call_ng_flags_prefix(out, s, "T38-", ng_t38_option, NULL)) return; if (call_ng_flags_prefix(out, s, "T.38-", ng_t38_option, NULL)) @@ -1082,6 +1088,8 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu call_ng_flags_list(out, dict, "transcode", call_ng_flags_codec_list, &out->codec_transcode); call_ng_flags_list(out, dict, "mask", call_ng_flags_str_ht, &out->codec_mask); call_ng_flags_list(out, dict, "set", call_ng_flags_str_ht_split, &out->codec_set); + call_ng_flags_list(out, dict, "accept", call_ng_flags_str_ht, &out->codec_accept); + call_ng_flags_list(out, dict, "consume", call_ng_flags_str_ht, &out->codec_consume); } #endif } @@ -1095,6 +1103,10 @@ static void call_ng_free_flags(struct sdp_ng_flags *flags) { g_hash_table_destroy(flags->codec_mask); if (flags->codec_set) g_hash_table_destroy(flags->codec_set); + if (flags->codec_accept) + g_hash_table_destroy(flags->codec_accept); + if (flags->codec_consume) + g_hash_table_destroy(flags->codec_consume); if (flags->sdes_no) g_hash_table_destroy(flags->sdes_no); g_queue_clear_full(&flags->codec_offer, free); diff --git a/daemon/codec.c b/daemon/codec.c index fc49c954b..c6d8dcd2d 100644 --- a/daemon/codec.c +++ b/daemon/codec.c @@ -425,6 +425,7 @@ static struct rtp_payload_type *__check_dest_codecs(struct call_media *receiver, const struct sdp_ng_flags *flags, GHashTable *supplemental_sinks, int *sink_transcoding) { struct rtp_payload_type *pref_dest_codec = NULL; + struct rtp_payload_type *first_tc_codec = NULL; for (GList *l = sink->codecs_prefs_send.head; l; l = l->next) { struct rtp_payload_type *pt = l->data; @@ -438,28 +439,53 @@ static struct rtp_payload_type *__check_dest_codecs(struct call_media *receiver, if (sink->ptime) pt->ptime = sink->ptime; - if (!pref_dest_codec && !pt->codec_def->supplemental) { - ilog(LOG_DEBUG, "Default sink codec is " STR_FORMAT, STR_FMT(&pt->encoding_with_params)); + if (!pref_dest_codec && !pt->codec_def->supplemental) pref_dest_codec = pt; - } + // also check if this is a transcoding codec: if we can send a codec to the sink, // but can't receive it on the receiver side, then it's transcoding. this is to check // whether transcoding on the sink side is actually needed. if transcoding has been // previously enabled on the sink, but no transcoding codecs are actually present, // we can disable the transcoding engine. + struct rtp_payload_type *recv_pt = g_hash_table_lookup(receiver->codecs_send, + &pt->payload_type); + if (recv_pt && rtp_payload_type_cmp(pt, recv_pt)) + recv_pt = NULL; + //ilog(LOG_DEBUG, "XXXXXXXXXXXX old flag is %i", *sink_transcoding); + //ilog(LOG_DEBUG, "XXXXXXXXXXXX checking dest codec " STR_FORMAT " is %i", + //STR_FMT(&pt->encoding_with_params), + //pt->for_transcoding); + //if (recv_pt) + //ilog(LOG_DEBUG, "XXXXXXXXXXXX checking dest codec reverse " STR_FORMAT " is %i", + //STR_FMT(&recv_pt->encoding_with_params), + //recv_pt->for_transcoding); if (MEDIA_ISSET(sink, TRANSCODE)) { - struct rtp_payload_type *recv_pt = g_hash_table_lookup(receiver->codecs_send, - &pt->payload_type); - if (!recv_pt || rtp_payload_type_cmp(pt, recv_pt)) { - // can the sink receive supplemental codec but the receiver can't send it? + if (!recv_pt) { + // can the sink receive codec but the receiver can't send it? *sink_transcoding |= 0x3; } } + if (pt->for_transcoding) { + // codec is explicitly marked for transcoding. enable transcoding engine + MEDIA_SET(receiver, TRANSCODE); + *sink_transcoding |= 0x3; + if (!first_tc_codec && !pt->codec_def->supplemental) + first_tc_codec = pt; + if (pt->codec_def->supplemental) + *sink_transcoding |= 0x4; + } + //ilog(LOG_DEBUG, "XXXXXXXXXXXX new flag is %i", *sink_transcoding); __track_supp_codec(supplemental_sinks, pt); } + if (first_tc_codec) + pref_dest_codec = first_tc_codec; + if (pref_dest_codec) + ilog(LOG_DEBUG, "Default sink codec is " STR_FORMAT, + STR_FMT(&pref_dest_codec->encoding_with_params)); + return pref_dest_codec; } @@ -474,10 +500,21 @@ static void __check_send_codecs(struct call_media *receiver, struct call_media * struct rtp_payload_type *recv_pt = g_hash_table_lookup(receiver->codecs_send, &pt->payload_type); int tc_flag = 0; + //ilog(LOG_DEBUG, "XXXXXXXXXXXX old flag is %i", *sink_transcoding); + //ilog(LOG_DEBUG, "XXXXXXXXXXXX checking send codec " STR_FORMAT " is %i", + //STR_FMT(&pt->encoding_with_params), + //pt->for_transcoding); + //if (recv_pt) + //ilog(LOG_DEBUG, "XXXXXXXXXXXX checking send codec reverse " STR_FORMAT " is %i", + //STR_FMT(&recv_pt->encoding_with_params), + //recv_pt->for_transcoding); if (!recv_pt || rtp_payload_type_cmp(pt, recv_pt)) tc_flag |= 0x3; if (flags && flags->inject_dtmf) tc_flag |= 0x1; + if (pt->for_transcoding) + tc_flag |= 0x3; + //ilog(LOG_DEBUG, "XXXXXXXXXXXX set flag is %i", *sink_transcoding); if (tc_flag) { // can the sink receive codec but the receiver can't send it? *sink_transcoding |= tc_flag; @@ -491,6 +528,7 @@ static void __check_send_codecs(struct call_media *receiver, struct call_media * g_hash_table_lookup(sink->codec_handlers, GINT_TO_POINTER(recv_pt->payload_type)); if (!ch_recv) continue; + //ilog(LOG_DEBUG, "XXXXXXXXXXXX handler transcoder %i", ch_recv->transcoder); if (ch_recv->transcoder) *sink_transcoding |= 0x3; } @@ -591,8 +629,28 @@ static void __single_codec(struct call_media *media, const struct sdp_ng_flags * } } +static int __check_receiver_codecs(struct call_media *receiver) { + int ret = 0; + // if some codecs were explicitly marked for transcoding, then we accept only those. + // otherwise we accept all that we can. + for (GList *l = receiver->codecs_prefs_send.head; l; l = l->next) { + struct rtp_payload_type *pt = l->data; + ensure_codec_def(pt, receiver); + if (!pt->codec_def) + continue; + //ilog(LOG_DEBUG, "XXXXXXXXXXXX checking recv send " STR_FORMAT " %i %i", STR_FMT(&pt->encoding_with_params), pt->for_transcoding, pt->codec_def->supplemental); + if (pt->for_transcoding) { + if (pt->codec_def->supplemental) + ret |= 0x2 | 0x4; + else + ret |= 0x1 | 0x2; + } + } + return ret; +} + static void __accept_transcode_codecs(struct call_media *receiver, struct call_media *sink, - const struct sdp_ng_flags *flags) + const struct sdp_ng_flags *flags, int accept_only_tc) { // if the other side is transcoding, we need to accept codecs that were // originally offered (recv->send) if we support them, even if the @@ -603,6 +661,9 @@ static void __accept_transcode_codecs(struct call_media *receiver, struct call_m ensure_codec_def(pt, receiver); if (!pt->codec_def) continue; + if (accept_only_tc && !pt->for_transcoding) + continue; + //ilog(LOG_DEBUG, "XXXXXXXXXXX accept codec " STR_FORMAT " flag %i", STR_FMT(&pt->encoding_with_params), pt->for_transcoding); struct rtp_payload_type *existing_pt = g_hash_table_lookup(receiver->codecs_recv, &pt->payload_type); if (existing_pt && !rtp_payload_type_cmp_nf(existing_pt, pt)) { @@ -645,6 +706,7 @@ static void __accept_transcode_codecs(struct call_media *receiver, struct call_m g_hash_table_insert(receiver->codecs_recv, &existing_pt->payload_type, existing_pt); } + //ilog(LOG_DEBUG, "XXXXXXXXXXXXX offered codec %i", pt->for_transcoding); ilog(LOG_DEBUG, "Accepting offered codec " STR_FORMAT " due to transcoding", STR_FMT(&pt->encoding_with_params)); MEDIA_SET(receiver, TRANSCODE); @@ -734,6 +796,7 @@ static GHashTable *__payload_type_queue_hash(GQueue *prefs, GQueue *order) { static void __symmetric_codecs(struct call_media *receiver, struct call_media *sink, int *sink_transcoding) { + //ilog(LOG_DEBUG, "XXXXXXXXXXXXXXX symm codec flags %i %i", MEDIA_ISSET(sink, TRANSCODE), *sink_transcoding); if (!MEDIA_ISSET(sink, TRANSCODE)) return; if (!*sink_transcoding) @@ -755,28 +818,62 @@ static void __symmetric_codecs(struct call_media *receiver, struct call_media *s // reconstruct list based on other side's preference. int transcoding = 0; + // keep track of our reconstruction order. there might be some codecs that have been force accepted + // that aren't present in sink->codecs_prefs_send. we must add them our output (receiver->send/recv) + // in order. + GList *prefix_pt_pos = prefs_send_order.head; + for (GList *l = sink->codecs_prefs_send.head; l; l = l->next) { struct rtp_payload_type *pt = l->data; + //ilog(LOG_DEBUG, "XXXXXXXXXXXXXXXX symm codec check " STR_FORMAT, STR_FMT(&pt->encoding_with_params)); // do we have a matching output? struct rtp_payload_type *out_pt = g_hash_table_lookup(prefs_recv, GINT_TO_POINTER(pt->payload_type)); - if (out_pt && g_hash_table_lookup(prefs_send, GINT_TO_POINTER(pt->payload_type))) { + struct rtp_payload_type *send_pt; + if (!out_pt || !(send_pt = g_hash_table_lookup(prefs_send, GINT_TO_POINTER(pt->payload_type)))) { + // we must transcode after all. + ilog(LOG_DEBUG, "RTP payload type %i is not symmetric and must be transcoded", + pt->payload_type); + transcoding = 1; + continue; + } + + // seek forward in our prefix list and check any PTs to see if they're force accepted + while (prefix_pt_pos) { + void *ptype = prefix_pt_pos->data; + struct rtp_payload_type *prefix_pt = g_hash_table_lookup(prefs_send, ptype); + prefix_pt_pos = prefix_pt_pos->next; + if (!prefix_pt) + continue; // bug? + if (prefix_pt == send_pt) + break; // caught up + //ilog(LOG_DEBUG, "XXXXXXXXXXXXXXXX prefix codec check " STR_FORMAT " %i", STR_FMT(&prefix_pt->encoding_with_params), prefix_pt->for_transcoding); + if (!prefix_pt->for_transcoding) + continue; // not interesting + // 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); - // and our send leg - out_pt = g_hash_table_lookup(prefs_send, GINT_TO_POINTER(pt->payload_type)); - if (out_pt) { - g_hash_table_steal(prefs_send, GINT_TO_POINTER(pt->payload_type)); - __rtp_payload_type_add_send(receiver, out_pt); + ilog(LOG_DEBUG, "Adding force-accepted RTP payload type %i", prefix_pt->payload_type); + g_hash_table_steal(prefs_send, ptype); + __rtp_payload_type_add_send(receiver, prefix_pt); + // and our receive leg + struct rtp_payload_type *in_pt = g_hash_table_lookup(prefs_recv, ptype); + if (in_pt) { + g_hash_table_steal(prefs_recv, ptype); + __rtp_payload_type_add_recv(receiver, in_pt, 1); } - continue; + transcoding = 1; + } + + // 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); + // and our send leg + out_pt = g_hash_table_lookup(prefs_send, GINT_TO_POINTER(pt->payload_type)); + if (out_pt) { + g_hash_table_steal(prefs_send, GINT_TO_POINTER(pt->payload_type)); + __rtp_payload_type_add_send(receiver, out_pt); } - // we must transcode after all. - ilog(LOG_DEBUG, "RTP payload type %i is not symmetric and must be transcoded", - pt->payload_type); - transcoding = 1; } if (!transcoding) @@ -1084,6 +1181,7 @@ int __supp_codec_match(struct call_media *receiver, struct call_media *sink, int { if (pt == -1) return 0; + //ilog(LOG_DEBUG, "XXXXXXXXX checking supp PT match %i", pt); struct rtp_payload_type *sink_pt_stor = NULL; struct rtp_payload_type *recv_pt_stor = NULL; @@ -1096,12 +1194,15 @@ int __supp_codec_match(struct call_media *receiver, struct call_media *sink, int *sink_pt = g_hash_table_lookup(sink->codecs_send, &pt); if (!*sink_pt) return 0; + //ilog(LOG_DEBUG, "XXXXXXXXX sink has supp PT %i", pt); // XXX should go by codec name/params, not payload type number *recv_pt = g_hash_table_lookup(receiver->codecs_recv, &pt); if (!*recv_pt) return 1; + //ilog(LOG_DEBUG, "XXXXXXXXX recv has supp PT %i", pt); if (rtp_payload_type_cmp(*sink_pt, *recv_pt)) return 1; + //ilog(LOG_DEBUG, "XXXXXXXXX recv has matching supp PT %i", pt); return 2; } @@ -1148,7 +1249,11 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink, // if we transcode, we transcode to the highest-preference supported codec // that the sink specified. determine this first. struct rtp_payload_type *pref_dest_codec = NULL; - int sink_transcoding = 0; // 0x1 = any transcoder present, 0x2 = non pseudo transcoder present + + // 0x1 = any transcoder present, 0x2 = non pseudo transcoder present, + // 0x4 = supplemental codec for transcoding + int sink_transcoding = 0; + // keep track of supplemental payload types. we hash them by clock rate // in case there's several of them. the clock rates of the destination // codec and the supplemental codec must match. @@ -1165,6 +1270,10 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink, // similarly, if the sink can receive a codec that the receiver can't send, it's also transcoding __check_send_codecs(receiver, sink, flags, supplemental_sinks, &sink_transcoding); + // 0x1 = accept only codecs marked for transcoding, 0x2 = some codecs marked for transcoding + // present, 0x4 = supplemental codec for transcoding + int receiver_transcoding = __check_receiver_codecs(receiver); + if (flags && flags->opmode == OP_ANSWER && flags->symmetric_codecs) __symmetric_codecs(receiver, sink, &sink_transcoding); @@ -1180,13 +1289,13 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink, int cn_pt_match = __supp_codec_match(receiver, sink, cn_payload_type, NULL, NULL); // stop transcoding if we've determined that we don't need it - if (MEDIA_ISSET(sink, TRANSCODE) && !sink_transcoding) { + if (MEDIA_ISSET(sink, TRANSCODE) && !sink_transcoding && !(receiver_transcoding & 0x2)) { ilog(LOG_DEBUG, "Disabling transcoding engine (not needed)"); MEDIA_CLEAR(sink, TRANSCODE); } if (MEDIA_ISSET(sink, TRANSCODE) && (sink_transcoding & 0x2)) - __accept_transcode_codecs(receiver, sink, flags); + __accept_transcode_codecs(receiver, sink, flags, (receiver_transcoding & 0x1)); else __eliminate_rejected_codecs(receiver, sink, flags); @@ -1196,6 +1305,8 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink, GHashTable *output_transcoders = g_hash_table_new(g_direct_hash, g_direct_equal); int transcode_supplemental = 0; // is one of our source codecs a supplemental one? + if ((sink_transcoding & 0x4)) + transcode_supplemental = 1; // do we need to detect PCM DTMF tones? int pcm_dtmf_detect = 0; @@ -1245,10 +1356,19 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink, goto next; } + //ilog(LOG_DEBUG, "XXXXXXXXXXXX pref dest codec " STR_FORMAT " is %i", + //STR_FMT(&pref_dest_codec->encoding_with_params), + //pref_dest_codec->for_transcoding); + struct rtp_payload_type *dest_pt; // transcode to this GQueue *dest_codecs = NULL; - if (!flags || !flags->always_transcode) { + if (pref_dest_codec->for_transcoding) { + // with always-transcode, we still accept DTMF payloads if possible + if (pt->codec_def && pt->codec_def->supplemental) + dest_codecs = g_hash_table_lookup(sink->codec_names_send, &pt->encoding); + } + else if (!flags || !flags->always_transcode) { // we ignore output codec matches if we must transcode supp codecs if ((dtmf_pt_match == 1 || cn_pt_match == 1) && MEDIA_ISSET(sink, TRANSCODE)) ; @@ -1374,6 +1494,12 @@ next: // if the sink does not support DTMF but we can receive it, we must transcode // DTMF event packets to PCM. this requires all codecs to be transcoded to the // sink's preferred destination codec. + //ilog(LOG_DEBUG, "XXXXXXXXXXXXX tc supp %i DTMF PT %i DTMF PT match %i PCM detect %i", + //transcode_supplemental, dtmf_payload_type, dtmf_pt_match, pcm_dtmf_detect); + //ilog(LOG_DEBUG, "XXXXXXXXXXXXX tc supp %i CN PT %i CN PT match %i", + //transcode_supplemental, cn_payload_type, cn_pt_match); + //ilog(LOG_DEBUG, "XXXXXXXXXXXXX %p %p %p", + //pref_dest_codec, handler->source_pt.codec_def, pref_dest_codec->codec_def); if (!transcode_supplemental && !pcm_dtmf_detect) __make_passthrough_ssrc(handler); else if (dtmf_pt_match == 2) @@ -1381,9 +1507,11 @@ next: else if (!pref_dest_codec || !handler->source_pt.codec_def || !pref_dest_codec->codec_def) __make_passthrough_ssrc(handler); - else + else { __make_transcoder(handler, pref_dest_codec, output_transcoders, dtmf_payload_type, pcm_dtmf_detect); + handler->cn_payload_type = cn_payload_type; + } passthrough_handlers = g_slist_delete_link(passthrough_handlers, passthrough_handlers); } @@ -2227,6 +2355,7 @@ static void __silence_detect_ ## type(struct codec_ssrc_handler *ch, AVFrame *fr last = NULL; \ \ for (unsigned int i = 0; i < frame->nb_samples; i++) { \ + /* ilog(LOG_DEBUG, "XXXXXXXXXXXX checking %u %i vs %i", i, (int) s[i], (int) thres); */ \ if (s[i] <= thres && s[1] >= -thres) { \ /* silence */ \ if (!last) { \ @@ -2253,6 +2382,7 @@ __silence_detect_type(int32_t) __silence_detect_type(int16_t) static void __silence_detect(struct codec_ssrc_handler *ch, AVFrame *frame) { + //ilog(LOG_DEBUG, "XXXXXXXXXXXXXXXXXXXX silence detect %i %i", rtpe_config.silence_detect_int, ch->handler->cn_payload_type); if (!rtpe_config.silence_detect_int) return; if (ch->handler->cn_payload_type < 0) @@ -3120,7 +3250,7 @@ int __codec_ht_except(int all_flag, GHashTable *yes_ht, GHashTable *no_ht, struc else if (g_hash_table_lookup(yes_ht, &pt->encoding_with_params)) do_this = 1; } - if (no_ht) { + if (no_ht && all_flag) { if (g_hash_table_lookup(no_ht, &pt->encoding)) do_this = 0; else if (g_hash_table_lookup(no_ht, &pt->encoding_with_params)) @@ -3128,6 +3258,19 @@ int __codec_ht_except(int all_flag, GHashTable *yes_ht, GHashTable *no_ht, struc } return do_this; } +void __ht_merge(GHashTable **dst, GHashTable *src) { + if (!src) + return; + if (!*dst) + *dst = g_hash_table_new_full(str_case_hash, str_case_equal, free, NULL); + GHashTableIter iter; + g_hash_table_iter_init(&iter, src); + void *key; + while (g_hash_table_iter_next(&iter, &key, NULL)) { + str *dup = str_dup(key); + g_hash_table_replace(*dst, dup, dup); + } +} void codec_rtp_payload_types(struct call_media *media, struct call_media *other_media, GQueue *types, struct sdp_ng_flags *flags) { @@ -3141,7 +3284,7 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ static const str str_full = STR_CONST_INIT("full"); GHashTable *stripped = g_hash_table_new_full(str_case_hash, str_case_equal, free, __payload_queue_free); GHashTable *masked = g_hash_table_new_full(str_case_hash, str_case_equal, free, __payload_queue_free); - int strip_all = 0, mask_all = 0; + int strip_all = 0, mask_all = 0, consume_all = 0; // start fresh if (!proto_is_rtp(other_media->protocol) && proto_is_rtp(media->protocol) && flags->opmode == OP_OFFER) { @@ -3164,12 +3307,21 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ if (flags->codec_strip && g_hash_table_lookup(flags->codec_strip, &str_all)) strip_all = 1; - if (flags->codec_strip && g_hash_table_lookup(flags->codec_strip, &str_all)) + else if (flags->codec_strip && g_hash_table_lookup(flags->codec_strip, &str_full)) strip_all = 2; if (flags->codec_mask && g_hash_table_lookup(flags->codec_mask, &str_all)) mask_all = 1; else if (flags->codec_mask && g_hash_table_lookup(flags->codec_mask, &str_full)) mask_all = 2; + if (flags->codec_consume && g_hash_table_lookup(flags->codec_consume, &str_all)) + consume_all = 1; + else if (flags->codec_consume && g_hash_table_lookup(flags->codec_consume, &str_full)) + consume_all = 2; + + __ht_merge(&flags->codec_except, flags->codec_consume); + __ht_merge(&flags->codec_except, flags->codec_accept); + __ht_merge(&flags->codec_except, flags->codec_strip); + __ht_merge(&flags->codec_except, flags->codec_mask); /* we steal the entire list to avoid duplicate allocs */ while ((pt = g_queue_pop_head(types))) { @@ -3204,6 +3356,28 @@ void codec_rtp_payload_types(struct call_media *media, struct call_media *other_ g_queue_push_tail(q, __rtp_payload_type_copy(pt)); __rtp_payload_type_add_send(other_media, pt); } + else if (__codec_ht_except(consume_all, flags->codec_consume, flags->codec_except, pt)) { + ilog(LOG_DEBUG, "Consuming codec '" STR_FORMAT "'", + STR_FMT(&pt->encoding_with_params)); +#ifdef WITH_TRANSCODING + codec_touched(pt, media); +#endif + pt->for_transcoding = 1; + GQueue *q = g_hash_table_lookup_queue_new(masked, str_dup(&pt->encoding), free); + g_queue_push_tail(q, __rtp_payload_type_copy(pt)); + q = g_hash_table_lookup_queue_new(masked, str_dup(&pt->encoding_with_params), free); + g_queue_push_tail(q, __rtp_payload_type_copy(pt)); + __rtp_payload_type_add_send(other_media, pt); + } + else if (__codec_ht_except(0, flags->codec_accept, NULL, pt)) { + ilog(LOG_DEBUG, "Accepting codec '" STR_FORMAT "'", + STR_FMT(&pt->encoding_with_params)); +#ifdef WITH_TRANSCODING + codec_touched(pt, media); +#endif + pt->for_transcoding = 1; + __rtp_payload_type_add(media, other_media, pt); + } else __rtp_payload_type_add(media, other_media, pt); } diff --git a/include/call_interfaces.h b/include/call_interfaces.h index 852b10048..8f3f8ea25 100644 --- a/include/call_interfaces.h +++ b/include/call_interfaces.h @@ -42,6 +42,8 @@ struct sdp_ng_flags { GQueue codec_offer; GQueue codec_transcode; GHashTable *codec_mask; + GHashTable *codec_accept; + GHashTable *codec_consume; GHashTable *codec_set; int ptime, rev_ptime; diff --git a/t/auto-daemon-tests.pl b/t/auto-daemon-tests.pl index f975eb1a0..7094e40e0 100755 --- a/t/auto-daemon-tests.pl +++ b/t/auto-daemon-tests.pl @@ -85,6 +85,93 @@ SDP new_call; +offer('codec-accept', + { codec => { accept => ['PCMU'] } }, < { consume => ['PCMU'] } }, < \$options{'codec-offer'}, 'codec-transcode=s@' => \$options{'codec-transcode'}, 'codec-mask=s@' => \$options{'codec-mask'}, + 'codec-consume=s@' => \$options{'codec-consume'}, + 'codec-accept=s@' => \$options{'codec-accept'}, 'codec-set=s@' => \$options{'codec-set'}, 'ptime=i' => \$options{'ptime'}, 'flags=s@' => \$options{'flags'}, @@ -106,7 +108,7 @@ if (defined($options{direction})) { $options{direction} =~ /(.*),(.*)/ or die; $packet{direction} = [$1,$2]; } -for my $x (qw(strip offer transcode mask set)) { +for my $x (qw(strip offer transcode mask set consume accept)) { if ($options{'codec-'.$x} && @{$options{'codec-'.$x}}) { if (!$options{'codec options flag'}) { $packet{codec}{$x} = $options{'codec-'.$x};