TT#30403 implement codec stripping

Change-Id: I384aa353b43986656145c443e40a2b96f04c489f
pull/445/head
Richard Fuchs 7 years ago
parent b8dbd997e9
commit d7dd7421e6

@ -1180,6 +1180,19 @@ Optionally included keys are:
See the `--recording-dir` option above.
* `codec`
Contains a dictionary controlling various aspects of codecs (or RTP payload types).
The following keys are understood:
* `strip`
Contains a list of strings. Each string is the name of a codec or RTP payload
type that should be removed from the SDP. Codec names are case sensitive, and
can be either from the list of codecs explicitly defined by the SDP through
an `a=rtpmap` attribute, or can be from the list of RFC-defined codecs. Examples
are `PCMU`, `opus`, or `telephone-event`.
An example of a complete `offer` request dictionary could be (SDP body abbreviated):

@ -1419,17 +1419,30 @@ static void __dtls_logic(const struct sdp_ng_flags *flags,
MEDIA_SET(other_media, DTLS);
}
static void __rtp_payload_types(struct call_media *media, GQueue *types) {
static void __rtp_payload_types(struct call_media *media, GQueue *types, GHashTable *strip) {
struct rtp_payload_type *pt;
struct call *call = media->call;
static const str __all = STR_CONST_INIT("all");
// start fresh
g_queue_clear(&media->rtp_payload_types_prefs);
/* we steal the entire list to avoid duplicate allocs */
while ((pt = g_queue_pop_head(types))) {
/* but we must duplicate the contents */
// codec stripping
if (strip) {
if (g_hash_table_lookup(strip, &pt->encoding)
|| g_hash_table_lookup(strip, &__all)) {
__payload_type_free(pt);
continue;
}
}
/* we must duplicate the contents */
call_str_cpy(call, &pt->encoding_with_params, &pt->encoding_with_params);
call_str_cpy(call, &pt->encoding, &pt->encoding);
call_str_cpy(call, &pt->encoding_parameters, &pt->encoding_parameters);
g_hash_table_replace(media->rtp_payload_types, &pt->payload_type, pt);
g_queue_push_tail(&media->rtp_payload_types_prefs, pt);
}
}
@ -1553,7 +1566,7 @@ int monologue_offer_answer(struct call_monologue *other_ml, GQueue *streams,
MEDIA_SET(other_media, SDES);
}
__rtp_payload_types(media, &sp->rtp_payload_types);
__rtp_payload_types(media, &sp->rtp_payload_types, flags->codec_strip);
/* send and recv are from our POV */
bf_copy_same(&media->media_flags, &sp->sp_flags,
@ -1963,6 +1976,7 @@ static void __call_free(void *p) {
g_queue_clear(&md->streams);
g_queue_clear(&md->endpoint_maps);
g_hash_table_destroy(md->rtp_payload_types);
g_queue_clear(&md->rtp_payload_types_prefs);
g_slice_free1(sizeof(*md), md);
}

@ -322,7 +322,9 @@ struct call_media {
GQueue streams; /* normally RTP + RTCP */
GQueue endpoint_maps;
GHashTable *rtp_payload_types;
GQueue rtp_payload_types_prefs;
volatile unsigned int media_flags;
};

@ -528,18 +528,18 @@ INLINE void ng_sdes_option(struct sdp_ng_flags *out, bencode_item_t *it, unsigne
static void call_ng_flags_list(struct sdp_ng_flags *out, bencode_item_t *input, const char *key,
void (*callback)(struct sdp_ng_flags *out, bencode_item_t *input))
void (*callback)(struct sdp_ng_flags *, bencode_item_t *, void *), void *parm)
{
bencode_item_t *list, *it;
if ((list = bencode_dictionary_get_expect(input, key, BENCODE_LIST))) {
for (it = list->child; it; it = it->sibling)
callback(out, it);
callback(out, it, parm);
}
}
static void call_ng_flags_sdes(struct sdp_ng_flags *out, bencode_item_t *it) {
static void call_ng_flags_sdes(struct sdp_ng_flags *out, bencode_item_t *it, void *dummy) {
ng_sdes_option(out, it, 0);
}
static void call_ng_flags_rtcp_mux(struct sdp_ng_flags *out, bencode_item_t *it) {
static void call_ng_flags_rtcp_mux(struct sdp_ng_flags *out, bencode_item_t *it, void *dummy) {
if (!bencode_strcmp(it, "offer"))
out->rtcp_mux_offer = 1;
else if (!bencode_strcmp(it, "require"))
@ -554,7 +554,7 @@ static void call_ng_flags_rtcp_mux(struct sdp_ng_flags *out, bencode_item_t *it)
ilog(LOG_WARN, "Unknown 'rtcp-mux' flag encountered: '"BENCODE_FORMAT"'",
BENCODE_FMT(it));
}
static void call_ng_flags_replace(struct sdp_ng_flags *out, bencode_item_t *it) {
static void call_ng_flags_replace(struct sdp_ng_flags *out, bencode_item_t *it, void *dummy) {
str_hyphenate(it);
if (!bencode_strcmp(it, "origin"))
out->replace_origin = 1;
@ -564,7 +564,29 @@ static void call_ng_flags_replace(struct sdp_ng_flags *out, bencode_item_t *it)
ilog(LOG_WARN, "Unknown 'replace' flag encountered: '"BENCODE_FORMAT"'",
BENCODE_FMT(it));
}
static void call_ng_flags_flags(struct sdp_ng_flags *out, bencode_item_t *it) {
static void call_ng_flags_codec_list(struct sdp_ng_flags *out, bencode_item_t *it, void *qp) {
str *s;
s = g_slice_alloc(sizeof(*s));
if (!bencode_get_str(it, s)) {
g_slice_free1(sizeof(*s), s);
return;
}
g_queue_push_tail((GQueue *) qp, s);
}
static void call_ng_flags_codec_ht_strip(bencode_item_t *it, GHashTable *ht, unsigned int strip) {
str *s;
s = g_slice_alloc(sizeof(*s));
if (!bencode_get_str(it, s)) {
g_slice_free1(sizeof(*s), s);
return;
}
str_shift(s, strip);
g_hash_table_replace(ht, s, s);
}
static void call_ng_flags_codec_ht(struct sdp_ng_flags *out, bencode_item_t *it, void *htp) {
call_ng_flags_codec_ht_strip(it, htp, 0);
}
static void call_ng_flags_flags(struct sdp_ng_flags *out, bencode_item_t *it, void *dummy) {
if (it->type != BENCODE_STRING)
return;
@ -586,8 +608,11 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, bencode_item_t *it) {
out->media_handover = 1;
else if (!bencode_strcmp(it, "reset"))
out->reset = 1;
// XXX unify these
else if (it->iov[1].iov_len >= 5 && !memcmp(it->iov[1].iov_base, "SDES-", 5))
ng_sdes_option(out, it, 5);
else if (it->iov[1].iov_len >= 12 && !memcmp(it->iov[1].iov_base, "codec-strip-", 12))
call_ng_flags_codec_ht_strip(it, out->codec_strip, 12);
else if (!bencode_strcmp(it, "port-latching"))
out->port_latching = 1;
else if (!bencode_strcmp(it, "record-call"))
@ -599,17 +624,18 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, bencode_item_t *it) {
BENCODE_FMT(it));
}
static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *input) {
bencode_item_t *list, *it;
bencode_item_t *list, *it, *dict;
int diridx;
str s;
ZERO(*out);
out->codec_strip = g_hash_table_new_full(str_hash, str_equal, str_slice_free, NULL);
out->trust_address = trust_address_def;
out->dtls_passive = dtls_passive_def;
call_ng_flags_list(out, input, "flags", call_ng_flags_flags);
call_ng_flags_list(out, input, "replace", call_ng_flags_replace);
call_ng_flags_list(out, input, "flags", call_ng_flags_flags, NULL);
call_ng_flags_list(out, input, "replace", call_ng_flags_replace, NULL);
diridx = 0;
if ((list = bencode_dictionary_get_expect(input, "direction", BENCODE_LIST))) {
@ -649,10 +675,10 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu
STR_FMT(&s));
}
call_ng_flags_list(out, input, "rtcp-mux", call_ng_flags_rtcp_mux);
call_ng_flags_list(out, input, "rtcp-mux", call_ng_flags_rtcp_mux, NULL);
/* XXX module still needs to support this list */
call_ng_flags_list(out, input, "SDES", call_ng_flags_sdes);
call_ng_flags_list(out, input, "SDES", call_ng_flags_sdes, NULL);
bencode_get_alt(input, "transport-protocol", "transport protocol", &out->transport_protocol_str);
out->transport_protocol = transport_protocol(&out->transport_protocol_str);
@ -662,6 +688,15 @@ static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *inpu
out->tos = bencode_dictionary_get_integer(input, "TOS", 256);
bencode_get_alt(input, "record-call", "record call", &out->record_call_str);
bencode_dictionary_get_str(input, "metadata", &out->metadata);
if ((dict = bencode_dictionary_get_expect(input, "codec", BENCODE_DICTIONARY))) {
call_ng_flags_list(out, dict, "strip", call_ng_flags_codec_ht, out->codec_strip);
call_ng_flags_list(out, dict, "offer", call_ng_flags_codec_list, &out->codec_offer);
}
}
static void call_ng_free_flags(struct sdp_ng_flags *flags) {
g_hash_table_destroy(flags->codec_strip);
g_queue_clear_full(&flags->codec_offer, str_slice_free);
}
static const char *call_offer_answer_ng(bencode_item_t *input,
@ -798,6 +833,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input,
if (ret == ERROR_NO_FREE_PORTS || ret == ERROR_NO_FREE_LOGS) {
ilog(LOG_ERR, "Destroying call");
errstr = "Ran out of ports";
call_destroy(call);
}
@ -811,6 +847,7 @@ static const char *call_offer_answer_ng(bencode_item_t *input,
out:
sdp_free(&parsed);
streams_free(&streams);
call_ng_free_flags(&flags);
return errstr;
}

@ -31,6 +31,8 @@ struct sdp_ng_flags {
int tos;
str record_call_str;
str metadata;
GHashTable *codec_strip;
GQueue codec_offer;
int asymmetric:1,
no_redis_update:1,
unidirectional:1,

@ -162,6 +162,13 @@ struct attribute_rtpmap {
struct rtp_payload_type rtp_pt;
};
struct attribute_fmtp {
str payload_type_str;
str format_parms_str;
unsigned int payload_type;
};
struct sdp_attribute {
str full_line, /* including a= and \r\n */
line_value, /* without a= and without \r\n */
@ -192,6 +199,7 @@ struct sdp_attribute {
ATTR_FINGERPRINT,
ATTR_SETUP,
ATTR_RTPMAP,
ATTR_FMTP,
ATTR_IGNORE,
ATTR_END_OF_CANDIDATES,
} attr;
@ -205,6 +213,7 @@ struct sdp_attribute {
struct attribute_fingerprint fingerprint;
struct attribute_setup setup;
struct attribute_rtpmap rtpmap;
struct attribute_fmtp fmtp;
} u;
};
@ -742,6 +751,26 @@ static int parse_attribute_rtpmap(struct sdp_attribute *output) {
return 0;
}
static int parse_attribute_fmtp(struct sdp_attribute *output) {
PARSE_DECL;
char *ep;
struct attribute_fmtp *a;
output->attr = ATTR_FMTP;
PARSE_INIT;
EXTRACT_TOKEN(u.fmtp.payload_type_str);
EXTRACT_TOKEN(u.fmtp.format_parms_str);
a = &output->u.fmtp;
a->payload_type = strtoul(a->payload_type_str.s, &ep, 10);
if (ep == a->payload_type_str.s)
return -1;
return 0;
}
static int parse_attribute(struct sdp_attribute *a) {
int ret;
@ -777,6 +806,8 @@ static int parse_attribute(struct sdp_attribute *a) {
ret = parse_attribute_rtcp(a);
else if (!str_cmp(&a->name, "ssrc"))
ret = parse_attribute_ssrc(a);
else if (!str_cmp(&a->name, "fmtp"))
ret = parse_attribute_fmtp(a);
break;
case 5:
if (!str_cmp(&a->name, "group"))
@ -1408,6 +1439,21 @@ static int replace_transport_protocol(struct sdp_chopper *chop,
return 0;
}
static int replace_codec_list(struct sdp_chopper *chop,
struct sdp_media *media, struct call_media *cm)
{
if (cm->rtp_payload_types_prefs.length == 0)
return 0; // legacy protocol or usage error
for (GList *l = cm->rtp_payload_types_prefs.head; l; l = l->next) {
struct rtp_payload_type *pt = l->data;
chopper_append_printf(chop, " %u", pt->payload_type);
}
if (skip_over(chop, &media->formats))
return -1;
return 0;
}
static int replace_media_port(struct sdp_chopper *chop, struct sdp_media *media, struct packet_stream *ps) {
str *port = &media->port;
unsigned int p;
@ -1643,6 +1689,22 @@ static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media *
goto strip; // hack/workaround: always remove a=mid
break;
case ATTR_RTPMAP:
if (media->rtp_payload_types_prefs.length == 0)
break; // legacy protocol or usage error
if (!g_hash_table_lookup(media->rtp_payload_types,
&attr->u.rtpmap.rtp_pt.payload_type))
goto strip;
break;
case ATTR_FMTP:
if (media->rtp_payload_types_prefs.length == 0)
break; // legacy protocol or usage error
if (!g_hash_table_lookup(media->rtp_payload_types,
&attr->u.fmtp.payload_type))
goto strip;
break;
default:
break;
}
@ -1951,6 +2013,8 @@ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologu
goto error;
if (replace_transport_protocol(chop, sdp_media, call_media))
goto error;
if (replace_codec_list(chop, sdp_media, call_media))
goto error;
if (sdp_media->connection.parsed) {
if (replace_network_address(chop, &sdp_media->connection.address, ps,

@ -44,6 +44,8 @@ GetOptions(
'reset' => \$options{'reset'},
'port-latching' => \$options{'port latching'},
'media-address=s' => \$options{'media address'},
'codec-strip=s@' => \$options{'codec-strip'},
'flags=s@' => \$options{'flags'},
) or die;
my $cmd = shift(@ARGV) or die;
@ -70,6 +72,12 @@ if (defined($options{direction})) {
$options{direction} =~ /(.*),(.*)/ or die;
$packet{direction} = [$1,$2];
}
if ($options{'codec-strip'} && @{$options{'codec-strip'}}) {
$packet{codec}{strip} = $options{'codec-strip'};
}
if ($options{'flags'} && @{$options{'flags'}}) {
push(@{$packet{flags}}, @{$options{'flags'}});
}
if (defined($options{sdp})) {
$packet{sdp} = $options{sdp};

Loading…
Cancel
Save