|
|
|
@ -151,12 +151,123 @@ 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) { |
|
|
|
static void dtmf_trigger_set(struct call *c, void *mlp) { |
|
|
|
struct call_monologue *ml = mlp; |
|
|
|
|
|
|
|
rwlock_lock_w(&c->master_lock); |
|
|
|
|
|
|
|
ilog(LOG_INFO, "Setting DTMF block mode to %i and setting new trigger to '" STR_FORMAT "'", |
|
|
|
ml->block_dtmf_trigger, STR_FMT(&ml->dtmf_trigger_end)); |
|
|
|
|
|
|
|
ml->block_dtmf = ml->block_dtmf_trigger; |
|
|
|
|
|
|
|
// switch trigger to end trigger |
|
|
|
ml->block_dtmf_trigger = ml->block_dtmf_trigger_end; |
|
|
|
ml->dtmf_trigger = ml->dtmf_trigger_end; |
|
|
|
ml->dtmf_trigger_end = STR_NULL; |
|
|
|
ml->dtmf_trigger_digits *= -1; // negative means it's active |
|
|
|
|
|
|
|
codec_update_all_handlers(ml); |
|
|
|
|
|
|
|
rwlock_unlock_w(&c->master_lock); |
|
|
|
} |
|
|
|
static void dtmf_trigger_unset(struct call *c, void *mlp) { |
|
|
|
struct call_monologue *ml = mlp; |
|
|
|
|
|
|
|
ilog(LOG_INFO, "Setting DTMF block mode to %i", ml->block_dtmf_trigger_end); |
|
|
|
|
|
|
|
rwlock_lock_w(&c->master_lock); |
|
|
|
|
|
|
|
ml->block_dtmf = ml->block_dtmf_trigger_end; |
|
|
|
ml->dtmf_trigger = STR_NULL; |
|
|
|
|
|
|
|
codec_update_all_handlers(ml); |
|
|
|
|
|
|
|
rwlock_unlock_w(&c->master_lock); |
|
|
|
} |
|
|
|
|
|
|
|
static void dtmf_check_trigger(struct call_media *media, char event, uint64_t ts, int clockrate) { |
|
|
|
struct call_monologue *ml = media->monologue; |
|
|
|
|
|
|
|
if (!clockrate) |
|
|
|
clockrate = 8000; |
|
|
|
|
|
|
|
if (!ml->dtmf_trigger.len) // do we have a trigger? |
|
|
|
return; |
|
|
|
if (ml->dtmf_trigger_match >= ml->dtmf_trigger.len) // is the trigger done already? |
|
|
|
return; |
|
|
|
|
|
|
|
// check delay from previous event |
|
|
|
if (media->dtmf_start) { |
|
|
|
uint32_t ts_diff = ts - media->dtmf_start; |
|
|
|
uint64_t ts_diff_ms = ts_diff * 1000 / clockrate; |
|
|
|
if (ts_diff_ms > rtpe_config.dtmf_digit_delay) { |
|
|
|
// delay too long: restart event trigger |
|
|
|
ml->dtmf_trigger_match = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (ml->dtmf_trigger_digits < 0) { |
|
|
|
// end trigger is active |
|
|
|
ml->dtmf_trigger_digits++; |
|
|
|
if (ml->dtmf_trigger_digits == 0) { |
|
|
|
// got all digits |
|
|
|
codec_timer_callback(ml->call, dtmf_trigger_unset, ml, 0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// is the new event a match? |
|
|
|
if (ml->dtmf_trigger.s[ml->dtmf_trigger_match] == event) { |
|
|
|
ml->dtmf_trigger_match++; |
|
|
|
if (ml->dtmf_trigger_match == ml->dtmf_trigger.len) { |
|
|
|
// trigger is finished |
|
|
|
ml->dtmf_trigger_match = 0; // reset |
|
|
|
|
|
|
|
ilog(LOG_INFO, "DTMF trigger ('" STR_FORMAT "') matched, setting block mode to %i", |
|
|
|
STR_FMT(&ml->dtmf_trigger), ml->block_dtmf_trigger); |
|
|
|
|
|
|
|
// We only hold a read-lock on the call here and cannot switch to a write-lock |
|
|
|
// easily, which is needed to reset the codec handlers. Therefore we do this |
|
|
|
// asynchronously: |
|
|
|
codec_timer_callback(ml->call, dtmf_trigger_set, ml, 0); |
|
|
|
|
|
|
|
// set up unblock triggers |
|
|
|
if (ml->block_dtmf_trigger_end_ms) |
|
|
|
codec_timer_callback(ml->call, dtmf_trigger_unset, ml, |
|
|
|
ml->block_dtmf_trigger_end_ms * 1000); |
|
|
|
} |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// can we do a partial match? |
|
|
|
for (size_t off = 1; off < ml->dtmf_trigger_match; off++) { |
|
|
|
// look for repeating prefix: trigger "ABCABD", matched 5, prefix at offset 3: [AB]C[AB] |
|
|
|
if (memcmp(ml->dtmf_trigger.s + off, ml->dtmf_trigger.s, ml->dtmf_trigger_match - off)) |
|
|
|
continue; |
|
|
|
// is the new event a match? |
|
|
|
unsigned int next_match_idx = ml->dtmf_trigger.len - off; |
|
|
|
if (ml->dtmf_trigger.s[next_match_idx] == event) { |
|
|
|
// got a partial match |
|
|
|
ml->dtmf_trigger_match = next_match_idx; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
// no partial match... reset completely |
|
|
|
if (event == ml->dtmf_trigger.s[0]) |
|
|
|
ml->dtmf_trigger_match = 1; |
|
|
|
else |
|
|
|
ml->dtmf_trigger_match = 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void dtmf_code_event(struct call_media *media, char event, uint64_t ts, int clockrate) { |
|
|
|
if (media->dtmf_code == event) // old/ongoing event |
|
|
|
return; |
|
|
|
|
|
|
|
// start of new event |
|
|
|
|
|
|
|
// check trigger before setting new dtmf_start |
|
|
|
dtmf_check_trigger(media, event, ts, clockrate); |
|
|
|
|
|
|
|
media->dtmf_code = event; |
|
|
|
media->dtmf_start = ts; |
|
|
|
media->dtmf_end = 0; |
|
|
|
@ -204,7 +315,7 @@ int dtmf_event_packet(struct media_packet *mp, str *payload, int clockrate, uint |
|
|
|
dtmf->event, dtmf->volume, dtmf->end, ntohs(dtmf->duration)); |
|
|
|
|
|
|
|
if (!dtmf->end) { |
|
|
|
dtmf_code_event(mp->media, dtmf_code_to_char(dtmf->event), ts); |
|
|
|
dtmf_code_event(mp->media, dtmf_code_to_char(dtmf->event), ts, clockrate); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
@ -251,7 +362,7 @@ void dtmf_dsp_event(const struct dtmf_event *new_event, struct dtmf_event *cur_e |
|
|
|
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); |
|
|
|
dtmf_code_event(media, (char) new_event->code, ts, clockrate); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|