TT#136956 support DTMF silence replacement

Change-Id: If693800a955a9ddf7245da0082426ae609deb407
pull/1430/head
Richard Fuchs 4 years ago
parent 478c2608e8
commit 38f97c2df8

@ -856,6 +856,12 @@ Optionally included keys are:
injection via the `play DTMF` control message. See `play DTMF` below for additional
information.
- `detect DTMF`
When present in a message that sets up codec handlers, enables
the DSP to detect in-band DTMF audio tones even when it
wouldn't otherwise be necessary.
- `generate RTCP`
Identical to setting `generate RTCP = on`.
@ -1513,6 +1519,22 @@ Optionally included keys are:
Enables media echo towards both the sender and the receiver of
this message.
* `DTMF-security`
Used in the `block DTMF` message to select the DTMF blocking mode. The
default mode is `drop` which simply drops DTMF event packets. The other
supported mode is `silence` which replaces DTMF events with silence
audio.
* `delay-buffer`
Takes an integer as value. When set to non-zero, enables the delay
buffer when setting up codec handlers. The delay buffer delays all
media by the given number of milliseconds before passing it on. Once
the delay buffer is configured, it must explicitly be disabled again by
setting this value to zero. The delay buffer setting is honoured in all
messages that set up codec handlers, such as `block DTMF`.
An example of a complete `offer` request dictionary could be (SDP body abbreviated):
{ "command": "offer", "call-id": "cfBXzDSZqhYNcXM", "from-tag": "mS9rSAn0Cr",

@ -29,6 +29,7 @@
#include "load.h"
#include "media_player.h"
#include "dtmf.h"
#include "codec.h"
struct fragment_key {
@ -866,8 +867,13 @@ static void call_ng_flags_flags(struct sdp_ng_flags *out, str *s, void *dummy) {
out->allow_transcoding = 1;
break;
case CSH_LOOKUP("inject-DTMF"):
case CSH_LOOKUP("inject-dtmf"):
out->inject_dtmf = 1;
break;
case CSH_LOOKUP("detect-DTMF"):
case CSH_LOOKUP("detect-dtmf"):
out->detect_dtmf = 1;
break;
case CSH_LOOKUP("pad-crypto"):
out->sdes_pad = 1;
break;
@ -951,6 +957,7 @@ void call_ng_flags_init(struct sdp_ng_flags *out, enum call_opmode opmode) {
out->dtls_reverse_passive = dtls_passive_def;
out->el_option = rtpe_config.endpoint_learning;
out->tos = 256;
out->delay_buffer = -1;
}
static void call_ng_dict_iter(struct sdp_ng_flags *out, bencode_item_t *input,
@ -1314,6 +1321,26 @@ static void call_ng_main_flags(struct sdp_ng_flags *out, str *key, bencode_item_
case CSH_LOOKUP("t.38"):
call_ng_flags_list(out, value, ng_t38_option, NULL);
break;
case CSH_LOOKUP("DTMF-security"):
case CSH_LOOKUP("dtmf-security"):
case CSH_LOOKUP("DTMF security"):
case CSH_LOOKUP("dtmf security"):
switch (__csh_lookup(&s)) {
case CSH_LOOKUP("drop"):
out->block_dtmf_mode = BLOCK_DTMF_DROP;
break;
case CSH_LOOKUP("silence"):
out->block_dtmf_mode = BLOCK_DTMF_SILENCE;
break;
default:
ilog(LOG_WARN, "Unknown 'DTMF-security' flag encountered: '" STR_FORMAT "'",
STR_FMT(&s));
}
break;
case CSH_LOOKUP("delay-buffer"):
case CSH_LOOKUP("delay buffer"):
out->delay_buffer = bencode_get_integer_str(value, out->delay_buffer);
break;
#endif
}
}
@ -2323,14 +2350,43 @@ const char *call_block_dtmf_ng(bencode_item_t *input, bencode_item_t *output) {
if (errstr)
return errstr;
enum block_dtmf_mode mode = BLOCK_DTMF_DROP;
if (flags.block_dtmf_mode)
mode = flags.block_dtmf_mode;
if (monologue) {
ilog(LOG_INFO, "Blocking directional DTMF (tag '" STR_FORMAT_M "')",
STR_FMT_M(&monologue->tag));
monologue->block_dtmf = BLOCK_DTMF_DROP;
monologue->block_dtmf = mode;
}
else {
ilog(LOG_INFO, "Blocking DTMF (entire call)");
call->block_dtmf = BLOCK_DTMF_DROP;
call->block_dtmf = mode;
}
if (is_pcm_dtmf_block_mode(mode) || flags.delay_buffer >= 0) {
if (monologue) {
if (flags.delay_buffer >= 0) {
for (GList *l = monologue->medias.head; l; l = l->next) {
struct call_media *media = l->data;
media->buffer_delay = flags.delay_buffer;
}
}
monologue->detect_dtmf = flags.detect_dtmf;
codec_update_all_handlers(monologue);
}
else
for (GList *l = call->monologues.head; l; l = l->next) {
struct call_monologue *ml = l->data;
ml->detect_dtmf = flags.detect_dtmf;
if (flags.delay_buffer >= 0) {
for (GList *k = ml->medias.head; k; k = k->next) {
struct call_media *media = k->data;
media->buffer_delay = flags.delay_buffer;
}
}
codec_update_all_handlers(ml);
}
}
return NULL;
@ -2349,15 +2405,41 @@ const char *call_unblock_dtmf_ng(bencode_item_t *input, bencode_item_t *output)
if (monologue) {
ilog(LOG_INFO, "Unblocking directional DTMF (tag '" STR_FORMAT_M "')",
STR_FMT_M(&monologue->tag));
enum block_dtmf_mode prev_mode = monologue->block_dtmf;
monologue->block_dtmf = BLOCK_DTMF_OFF;
if (is_pcm_dtmf_block_mode(prev_mode) || flags.delay_buffer >= 0) {
if (flags.delay_buffer >= 0) {
for (GList *l = monologue->medias.head; l; l = l->next) {
struct call_media *media = l->data;
media->buffer_delay = flags.delay_buffer;
}
}
monologue->detect_dtmf = flags.detect_dtmf;
codec_update_all_handlers(monologue);
}
}
else {
ilog(LOG_INFO, "Unblocking DTMF (entire call)");
enum block_dtmf_mode prev_mode = call->block_dtmf;
call->block_dtmf = BLOCK_DTMF_OFF;
if (flags.all) {
if (flags.all || is_pcm_dtmf_block_mode(prev_mode) || flags.delay_buffer >= 0) {
for (GList *l = call->monologues.head; l; l = l->next) {
monologue = l->data;
monologue->block_dtmf = BLOCK_DTMF_OFF;
enum block_dtmf_mode prev_ml_mode = BLOCK_DTMF_OFF;
if (flags.all) {
prev_ml_mode = monologue->block_dtmf;
monologue->block_dtmf = BLOCK_DTMF_OFF;
}
if (flags.delay_buffer >= 0) {
for (GList *k = monologue->medias.head; k; k = k->next) {
struct call_media *media = k->data;
media->buffer_delay = flags.delay_buffer;
}
}
monologue->detect_dtmf = flags.detect_dtmf;
if (is_pcm_dtmf_block_mode(prev_ml_mode) || is_pcm_dtmf_block_mode(prev_mode)
|| flags.delay_buffer >= 0)
codec_update_all_handlers(monologue);
}
}
}

@ -36,7 +36,7 @@ struct mqtt_timer {
typedef void (*raw_input_func_t)(struct media_packet *mp, unsigned int);
static void __buffer_delay_raw(struct delay_buffer *dbuf,
raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate);
raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate, uint32_t);
static codec_handler_func handler_func_passthrough;
@ -133,6 +133,7 @@ struct delay_frame {
AVFrame *frame;
struct media_packet mp;
unsigned int clockrate;
uint32_t ts;
encoder_input_func_t encoder_func;
raw_input_func_t raw_func;
struct codec_handler *handler;
@ -984,6 +985,9 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
AUTO_CLEANUP(GHashTable *output_transcoders, __g_hash_table_destroy)
= g_hash_table_new(g_direct_hash, g_direct_equal);
enum block_dtmf_mode dtmf_block_mode = dtmf_get_block_mode(NULL, receiver->monologue);
bool do_pcm_dtmf_blocking = is_pcm_dtmf_block_mode(dtmf_block_mode);
for (GList *l = receiver->codecs.codec_prefs.head; l; ) {
struct rtp_payload_type *pt = l->data;
@ -1052,6 +1056,10 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
sink_pt = pref_dest_codec;
}
// ignore DTMF sink if we're blocking DTMF in PCM replacement mode
if (do_pcm_dtmf_blocking && sink_pt && sink_pt->codec_def && sink_pt->codec_def->dtmf)
sink_pt = NULL;
// still no output? pick the preferred sink codec
if (!sink_pt)
sink_pt = pref_dest_codec;
@ -1098,6 +1106,15 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
if (receiver->monologue->detect_dtmf)
pcm_dtmf_detect = true;
// special mode for DTMF blocking
if (do_pcm_dtmf_blocking) {
sink_dtmf_pt = NULL; // always transcode DTMF to PCM
// enable DSP if we expect DTMF to be carried as PCM
if (!recv_dtmf_pt)
pcm_dtmf_detect = true;
}
if (pcm_dtmf_detect) {
if (sink_dtmf_pt)
ilogs(codec, LOG_DEBUG, "Enabling PCM DTMF detection from " STR_FORMAT
@ -1144,7 +1161,7 @@ void codec_handlers_update(struct call_media *receiver, struct call_media *sink,
// DTMF
if (pcm_dtmf_detect)
goto transcode;
if (recv_dtmf_pt && recv_dtmf_pt->for_transcoding && !sink_dtmf_pt) {
if (recv_dtmf_pt && (recv_dtmf_pt->for_transcoding || do_pcm_dtmf_blocking) && !sink_dtmf_pt) {
ilogs(codec, LOG_DEBUG, "Transcoding DTMF events to PCM from " STR_FORMAT
" to " STR_FORMAT,
STR_FMT(&pt->encoding_with_params),
@ -1410,12 +1427,14 @@ static int handler_func_passthrough(struct codec_handler *h, struct media_packet
if (!handler_silence_block(h, mp))
return 0;
uint32_t ts = 0;
if (mp->rtp) {
codec_calc_jitter(mp->ssrc_in, ntohl(mp->rtp->timestamp), h->source_pt.clock_rate, &mp->tv);
ts = ntohl(mp->rtp->timestamp);
codec_calc_jitter(mp->ssrc_in, ts, h->source_pt.clock_rate, &mp->tv);
codec_calc_lost(mp->ssrc_in, ntohs(mp->rtp->seq_num));
}
__buffer_delay_raw(h->delay_buffer, codec_add_raw_packet, mp, h->source_pt.clock_rate);
__buffer_delay_raw(h->delay_buffer, codec_add_raw_packet, mp, h->source_pt.clock_rate, ts);
return 0;
}
@ -1842,7 +1861,7 @@ static int packet_dtmf_event(struct codec_ssrc_handler *ch, struct codec_ssrc_ha
LOCK(&mp->media->dtmf_lock);
if (mp->media->dtmf_ts != packet->ts) { // ignore already processed events
int ret = dtmf_event_packet(mp, packet->payload, ch->encoder_format.clockrate);
int ret = dtmf_event_packet(mp, packet->payload, ch->encoder_format.clockrate, packet->ts);
if (G_UNLIKELY(ret == -1)) // error
return -1;
if (ret == 1) {
@ -2074,8 +2093,10 @@ static int handler_func_passthrough_ssrc(struct codec_handler *h, struct media_p
if (!handler_silence_block(h, mp))
return 0;
uint32_t ts = 0;
if (mp->rtp) {
codec_calc_jitter(mp->ssrc_in, ntohl(mp->rtp->timestamp), h->source_pt.clock_rate, &mp->tv);
ts = ntohl(mp->rtp->timestamp);
codec_calc_jitter(mp->ssrc_in, ts, h->source_pt.clock_rate, &mp->tv);
codec_calc_lost(mp->ssrc_in, ntohs(mp->rtp->seq_num));
}
@ -2085,7 +2106,7 @@ static int handler_func_passthrough_ssrc(struct codec_handler *h, struct media_p
// keep track of other stats here?
__buffer_delay_raw(h->delay_buffer, codec_add_raw_packet, mp, h->source_pt.clock_rate);
__buffer_delay_raw(h->delay_buffer, codec_add_raw_packet, mp, h->source_pt.clock_rate, ts);
return 0;
}
@ -2116,7 +2137,8 @@ void codec_add_dtmf_event(struct codec_ssrc_handler *ch, int code, int level, ui
struct dtmf_event new_ev = { .code = code, .volume = level, .ts = ts };
ilogs(transcoding, LOG_DEBUG, "DTMF event state change: code %i, volume %i, TS %lu",
new_ev.code, new_ev.volume, (unsigned long) ts);
dtmf_dsp_event(&new_ev, &ch->dtmf_state, ch->handler->media, ch->handler->source_pt.clock_rate);
dtmf_dsp_event(&new_ev, &ch->dtmf_state, ch->handler->media, ch->handler->source_pt.clock_rate,
ts + ch->first_ts);
// add to queue if we're doing PCM -> DTMF event conversion
if (ch->handler && ch->handler->dtmf_payload_type != -1) {
@ -2203,7 +2225,7 @@ static int delay_frame_cmp(const void *A, const void *B, void *ptr) {
// consumes frame
static void __buffer_delay_frame(struct delay_buffer *dbuf, struct codec_ssrc_handler *ch,
encoder_input_func_t input_func, AVFrame *frame, struct media_packet *mp)
encoder_input_func_t input_func, AVFrame *frame, struct media_packet *mp, uint32_t ts)
{
if (__buffer_delay_do_direct(dbuf)) {
// input now
@ -2215,6 +2237,7 @@ static void __buffer_delay_frame(struct delay_buffer *dbuf, struct codec_ssrc_ha
struct delay_frame *dframe = g_slice_alloc0(sizeof(*dframe));
dframe->frame = frame;
dframe->encoder_func = input_func;
dframe->ts = ts;
dframe->ch = obj_get(&ch->h);
media_packet_copy(&dframe->mp, mp);
@ -2226,7 +2249,7 @@ static void __buffer_delay_frame(struct delay_buffer *dbuf, struct codec_ssrc_ha
}
static void __buffer_delay_raw(struct delay_buffer *dbuf,
raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate)
raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate, uint32_t ts)
{
if (__buffer_delay_do_direct(dbuf)) {
// direct passthrough
@ -2349,6 +2372,25 @@ static void dtx_buffer_stop(struct dtx_buffer **dtxbp) {
}
static void delay_frame_manipulate(struct delay_frame *dframe) {
struct call_media *media = dframe->mp.media;
if (!media)
return;
AVFrame *frame = dframe->frame;
if (is_in_dtmf_event(media, dframe->ts, frame->sample_rate, media->buffer_delay, media->buffer_delay)) {
enum block_dtmf_mode mode = dtmf_get_block_mode(dframe->mp.call, media->monologue);
switch (mode) {
case BLOCK_DTMF_SILENCE:
memset(frame->extended_data[0], 0, frame->linesize[0]);
break;
default:
break;
}
}
}
static void __delay_frame_process(struct delay_buffer *dbuf, struct delay_frame *dframe) {
if (dframe->mp.rtp) {
// adjust output seq num. pushing packets into the delay buffer looks as if they
@ -2361,7 +2403,8 @@ static void __delay_frame_process(struct delay_buffer *dbuf, struct delay_frame
struct codec_ssrc_handler *csh = dframe->ch;
if (csh && csh->handler && csh->encoder) {
if (csh && csh->handler && csh->encoder && dframe->encoder_func) {
delay_frame_manipulate(dframe);
dframe->encoder_func(csh->encoder, dframe->frame, csh->handler->packet_encoded,
csh, &dframe->mp);
}
@ -3224,7 +3267,8 @@ static int packet_decoded_common(decoder_t *decoder, AVFrame *frame, void *u1, v
if (mp->media_out)
ch->encoder->codec_options.amr.cmr = mp->media_out->u.amr.cmr;
__buffer_delay_frame(h->delay_buffer, ch, input_func, frame, mp);
uint32_t ts = mp->rtp ? ntohl(mp->rtp->timestamp) : frame->pts;
__buffer_delay_frame(h->delay_buffer, ch, input_func, frame, mp, ts);
frame = NULL; // consumed
discard:
@ -3294,7 +3338,7 @@ out:
// dummy/stub
static void __buffer_delay_raw(struct delay_buffer *dbuf,
raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate)
raw_input_func_t input_func, struct media_packet *mp, unsigned int clockrate, uint32_t ts)
{
input_func(mp, clockrate);
}

@ -29,6 +29,13 @@ static unsigned int dtmf_volume_from_dsp(int vol) {
else
return 63;
}
static char dtmf_code_to_char(int code) {
static const char codes[] = "0123456789*#ABCD";
if (code < 0 || code > 15)
return 0;
return codes[code];
}
static void dtmf_bencode_and_notify(struct call_media *media, unsigned int event, unsigned int volume,
unsigned int duration, const endpoint_t *fsin, int clockrate)
@ -119,11 +126,17 @@ bool dtmf_do_logging(void) {
}
static void dtmf_end_event(struct call_media *media, unsigned int event, unsigned int volume,
unsigned int duration, const endpoint_t *fsin, int clockrate, bool rfc_event)
unsigned int duration, const endpoint_t *fsin, int clockrate, bool rfc_event, uint64_t ts)
{
if (!clockrate)
clockrate = 8000;
media->dtmf_code = 0;
media->dtmf_end = ts;
if (!dtmf_do_logging())
return;
GString *buf = dtmf_json_print(media, event, volume, duration, fsin, clockrate);
if (_log_facility_dtmf)
@ -138,8 +151,47 @@ static void dtmf_end_event(struct call_media *media, unsigned int event, unsigne
g_string_free(buf, TRUE);
}
static void dtmf_code_event(struct call_media *media, char event, uint64_t ts) {
if (media->dtmf_code == event) // old/ongoing event
return;
// start of new event
media->dtmf_code = event;
media->dtmf_start = ts;
media->dtmf_end = 0;
}
bool is_in_dtmf_event(struct call_media *media, uint32_t ts, int clockrate, unsigned int head,
unsigned int trail)
{
if (!clockrate)
clockrate = 8000;
uint32_t start_ts = ts + head * clockrate / 1000;
uint32_t end_ts = ts - trail * clockrate / 1000;
if (!media->dtmf_start)
return false;
if (media->dtmf_code) {
// active event. is it current?
uint32_t start_diff = start_ts - media->dtmf_start;
if (start_diff > clockrate * 10)
return false; // outdated start TS
}
else {
// event has already ended. did it just end now?
uint32_t end_diff = media->dtmf_end - end_ts;
if (end_diff > clockrate * 10)
return false; // bad or old end TS
}
return true;
}
int dtmf_event_packet(struct media_packet *mp, str *payload, int clockrate) {
int dtmf_event_packet(struct media_packet *mp, str *payload, int clockrate, uint64_t ts) {
struct telephone_event_payload *dtmf;
if (payload->len < sizeof(*dtmf)) {
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Short DTMF event packet (len %zu)", payload->len);
@ -150,19 +202,18 @@ int dtmf_event_packet(struct media_packet *mp, str *payload, int clockrate) {
ilog(LOG_DEBUG, "DTMF event packet: event %u, volume %u, end %u, duration %u",
dtmf->event, dtmf->volume, dtmf->end, ntohs(dtmf->duration));
if (!dtmf->end)
if (!dtmf->end) {
dtmf_code_event(mp->media, dtmf_code_to_char(dtmf->event), ts);
return 0;
}
if (!dtmf_do_logging())
return 1;
dtmf_end_event(mp->media, dtmf->event, dtmf->volume, dtmf->duration, &mp->fsin, clockrate, true);
dtmf_end_event(mp->media, dtmf->event, dtmf->volume, dtmf->duration, &mp->fsin, clockrate, true, ts);
return 1;
}
void dtmf_dsp_event(const struct dtmf_event *new_event, struct dtmf_event *cur_event_p,
struct call_media *media, int clockrate)
struct call_media *media, int clockrate, uint64_t ts)
{
// update state tracker regardless of outcome
struct dtmf_event cur_event = *cur_event_p;
@ -170,9 +221,15 @@ void dtmf_dsp_event(const struct dtmf_event *new_event, struct dtmf_event *cur_e
if (!media)
return;
// we only care for "end" events
if (cur_event.code == 0 || new_event->code != 0)
return;
bool end_event;
if (cur_event.code != 0 && new_event->code == 0)
end_event = true;
else if (cur_event.code == 0 && new_event->code != 0)
end_event = false; // start of a new code
else
return; // don't care
if (!media->streams.length)
return;
@ -181,14 +238,20 @@ void dtmf_dsp_event(const struct dtmf_event *new_event, struct dtmf_event *cur_e
unsigned int duration = cur_event.ts - new_event->ts;
ilog(LOG_DEBUG, "DTMF DSP end event: event %u, volume %u, duration %u",
cur_event.code, cur_event.volume, duration);
if (!dtmf_do_logging())
return;
if (end_event) {
ilog(LOG_DEBUG, "DTMF DSP end event: event %u, volume %u, duration %u",
cur_event.code, cur_event.volume, duration);
dtmf_end_event(media, dtmf_code_from_char(cur_event.code), dtmf_volume_from_dsp(cur_event.volume),
duration, &ps->endpoint, clockrate, false);
dtmf_end_event(media, dtmf_code_from_char(cur_event.code), dtmf_volume_from_dsp(cur_event.volume),
duration, &ps->endpoint, clockrate, false, ts);
}
else {
ilog(LOG_DEBUG, "DTMF DSP code event: event %u, volume %u, duration %u",
new_event->code, new_event->volume, duration);
int code = dtmf_code_from_char(new_event->code); // for validation
if (code != -1)
dtmf_code_event(media, (char) new_event->code, ts);
}
}
void dtmf_event_free(void *e) {
@ -261,13 +324,6 @@ int dtmf_code_from_char(char c) {
#ifdef WITH_TRANSCODING
static char dtmf_code_to_char(int code) {
static const char codes[] = "0123456789*#ABCD";
if (code < 0 || code > 15)
return 0;
return codes[code];
}
// takes over the csh reference
static const char *dtmf_inject_pcm(struct call_media *media, struct call_media *sink,
struct call_monologue *monologue,
@ -440,3 +496,9 @@ enum block_dtmf_mode dtmf_get_block_mode(struct call *call, struct call_monologu
return BLOCK_DTMF_OFF;
return ml->block_dtmf;
}
bool is_pcm_dtmf_block_mode(enum block_dtmf_mode mode) {
if (mode >= BLOCK_DTMF___PCM_REPLACE_START && mode <= BLOCK_DTMF___PCM_REPLACE_END)
return true;
return false;
}

@ -202,6 +202,10 @@ enum {
enum block_dtmf_mode {
BLOCK_DTMF_OFF = 0,
BLOCK_DTMF_DROP = 1,
BLOCK_DTMF___PCM_REPLACE_START = 2,
BLOCK_DTMF_SILENCE = 2,
BLOCK_DTMF___PCM_REPLACE_END = 2,
};
@ -393,6 +397,9 @@ struct call_media {
mutex_t dtmf_lock;
unsigned long dtmf_ts; // TS of last processed end event
uint32_t dtmf_start;
char dtmf_code;
uint32_t dtmf_end;
#ifdef WITH_TRANSCODING
union {
struct {

@ -76,6 +76,8 @@ struct sdp_ng_flags {
MEO_BOTH,
} media_echo:3;
enum endpoint_learning el_option;
enum block_dtmf_mode block_dtmf_mode;
int delay_buffer;
unsigned int asymmetric:1,
protocol_accept:1,
no_redis_update:1,
@ -120,6 +122,7 @@ struct sdp_ng_flags {
allow_transcoding:1,
accept_any:1,
inject_dtmf:1,
detect_dtmf:1,
t38_decode:1,
t38_force:1,
t38_stop:1,

@ -22,7 +22,7 @@ struct dtmf_event {
};
void dtmf_init(void);
int dtmf_event_packet(struct media_packet *, str *, int); // 0 = ok, 1 = end event, -1 = error
int dtmf_event_packet(struct media_packet *, str *, int, uint64_t ts); // 0 = ok, 1 = end event, -1 = error
int dtmf_event_payload(str *, uint64_t *, uint64_t, struct dtmf_event *, GQueue *);
void dtmf_event_free(void *);
int dtmf_code_from_char(char);
@ -30,7 +30,9 @@ const char *dtmf_inject(struct call_media *media, int code, int volume, int dura
struct call_media *sink);
bool dtmf_do_logging(void);
void dtmf_dsp_event(const struct dtmf_event *new_event, struct dtmf_event *cur_event,
struct call_media *media, int clockrate);
struct call_media *media, int clockrate, uint64_t ts);
enum block_dtmf_mode dtmf_get_block_mode(struct call *call, struct call_monologue *ml);
bool is_pcm_dtmf_block_mode(enum block_dtmf_mode mode);
bool is_in_dtmf_event(struct call_media *, uint32_t ts, int clockrate, unsigned int head, unsigned int trail);
#endif

@ -89,6 +89,7 @@ GetOptions(
'symmetric-codecs' => \$options{'symmetric codecs'},
'asymmetric-codecs' => \$options{'asymmetric codecs'},
'inject-DTMF' => \$options{'inject DTMF'},
'detect-DTMF' => \$options{'detect DTMF'},
'DTLS-fingerprint=s' => \$options{'DTLS-fingerprint'},
'ICE-lite=s' => \$options{'ICE-lite'},
'no-jitter-buffer' => \$options{'no jitter buffer'},
@ -103,13 +104,15 @@ GetOptions(
'from-label=s' => \$options{'from-label'},
'to-label=s' => \$options{'to-label'},
'from-tags=s@' => \$options{'from-tags'},
'DTMF-security=s' => \$options{'DTMF-security'},
'delay-buffer=i' => \$options{'delay-buffer'},
) or die;
my $cmd = shift(@ARGV) or die;
my %packet = (command => $cmd);
for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address,ptime,xmlrpc-callback,metadata,address,file,db-id,code,DTLS-fingerprint,ICE-lite,media echo,label,set-label,from-label,to-label')) {
for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address,ICE,address family,DTLS,via-branch,media address,ptime,xmlrpc-callback,metadata,address,file,db-id,code,DTLS-fingerprint,ICE-lite,media echo,label,set-label,from-label,to-label,DTMF-security')) {
if (defined($options{$x})) {
if (!$options{json}) {
$packet{$x} = \$options{$x};
@ -119,10 +122,10 @@ for my $x (split(/,/, 'from-tag,to-tag,call-id,transport protocol,media address,
}
}
}
for my $x (split(/,/, 'TOS,delete-delay')) {
for my $x (split(/,/, 'TOS,delete-delay,delay-buffer')) {
defined($options{$x}) and $packet{$x} = $options{$x};
}
for my $x (split(/,/, 'trust address,symmetric,asymmetric,unidirectional,force,strict source,media handover,sip source address,reset,port latching,no rtcp attribute,full rtcp attribute,loop protect,record call,always transcode,all,SIPREC,pad crypto,generate mid,fragment,original sendrecv,symmetric codecs,asymmetric codecs,inject DTMF,generate RTCP,single codec,reorder codecs,pierce NAT,SIP-source-address,allow transcoding')) {
for my $x (split(/,/, 'trust address,symmetric,asymmetric,unidirectional,force,strict source,media handover,sip source address,reset,port latching,no rtcp attribute,full rtcp attribute,loop protect,record call,always transcode,all,SIPREC,pad crypto,generate mid,fragment,original sendrecv,symmetric codecs,asymmetric codecs,inject DTMF,detect DTMF,generate RTCP,single codec,reorder codecs,pierce NAT,SIP-source-address,allow transcoding')) {
defined($options{$x}) and push(@{$packet{flags}}, $x);
}
for my $x (split(/,/, 'origin,session connection,sdp version,username,session-name,zero-address')) {

Loading…
Cancel
Save