mirror of https://github.com/sipwise/rtpengine.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
143 lines
3.8 KiB
143 lines
3.8 KiB
#include "dtmf.h"
|
|
#include "media_socket.h"
|
|
#include "log.h"
|
|
#include "call.h"
|
|
#include "dtmflib.h"
|
|
|
|
|
|
|
|
static GString *dtmf_json_print(struct media_packet *mp,
|
|
struct telephone_event_payload *dtmf, int clockrate)
|
|
{
|
|
if (!dtmf->end)
|
|
return NULL;
|
|
|
|
GString *buf = g_string_new("");
|
|
|
|
if (!clockrate)
|
|
clockrate = 8000;
|
|
|
|
g_string_append_printf(buf, "{"
|
|
"\"callid\":\"" STR_FORMAT "\","
|
|
"\"source_tag\":\"" STR_FORMAT "\","
|
|
"\"tags\":[",
|
|
STR_FMT(&mp->call->callid),
|
|
STR_FMT(&mp->media->monologue->tag));
|
|
|
|
GList *tag_values = g_hash_table_get_values(mp->call->tags);
|
|
int i = 0;
|
|
for (GList *tag_it = tag_values; tag_it; tag_it = tag_it->next) {
|
|
struct call_monologue *ml = tag_it->data;
|
|
if (i != 0)
|
|
g_string_append(buf, ",");
|
|
g_string_append_printf(buf, "\"" STR_FORMAT "\"",
|
|
STR_FMT(&ml->tag));
|
|
i++;
|
|
}
|
|
g_list_free(tag_values);
|
|
|
|
g_string_append_printf(buf, "],"
|
|
"\"type\":\"DTMF\",\"timestamp\":%lu,\"source_ip\":\"%s\","
|
|
"\"event\":%u,\"duration\":%u,\"volume\":%u}",
|
|
(unsigned long) rtpe_now.tv_sec,
|
|
sockaddr_print_buf(&mp->fsin.address),
|
|
(unsigned int) dtmf->event,
|
|
(ntohs(dtmf->duration) * (1000000 / clockrate)) / 1000,
|
|
(unsigned int) dtmf->volume);
|
|
|
|
return buf;
|
|
}
|
|
|
|
int dtmf_event(struct media_packet *mp, str *payload, int clockrate) {
|
|
struct telephone_event_payload *dtmf;
|
|
if (payload->len < sizeof(*dtmf)) {
|
|
ilog(LOG_WARN | LOG_FLAG_LIMIT, "Short DTMF event packet (len %u)", payload->len);
|
|
return -1;
|
|
}
|
|
dtmf = (void *) payload->s;
|
|
|
|
ilog(LOG_DEBUG, "DTMF event: event %u, volume %u, end %u, duration %u",
|
|
dtmf->event, dtmf->volume, dtmf->end, dtmf->duration);
|
|
|
|
int ret = 0;
|
|
|
|
if (_log_facility_dtmf) {
|
|
GString *buf = dtmf_json_print(mp, dtmf, clockrate);
|
|
if (buf) {
|
|
dtmflog(buf);
|
|
ret = 1; // END event
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void dtmf_event_free(void *e) {
|
|
g_slice_free1(sizeof(struct dtmf_event), e);
|
|
}
|
|
|
|
// returns: 0 = no DTMF. 1 = DTMF start event. 2 = DTMF in progress. 3 = DTMF end event.
|
|
int dtmf_event_payload(str *buf, uint64_t *pts, uint64_t duration, struct dtmf_event *cur_event, GQueue *events) {
|
|
// do we have a relevant state change?
|
|
struct dtmf_event prev_event = *cur_event;
|
|
while (events->length) {
|
|
struct dtmf_event *ev = g_queue_peek_head(events);
|
|
ilog(LOG_DEBUG, "Next DTMF event starts at %lu. PTS now %li", (unsigned long) ev->ts,
|
|
(unsigned long) *pts);
|
|
if (ev->ts > *pts)
|
|
break; // future event
|
|
|
|
ilog(LOG_DEBUG, "DTMF state change at %lu: %i -> %i, duration %lu", (unsigned long) ev->ts,
|
|
cur_event->code, ev->code, (unsigned long) duration);
|
|
g_queue_pop_head(events);
|
|
*cur_event = *ev;
|
|
dtmf_event_free(ev);
|
|
cur_event->ts = *pts; // canonicalise start TS
|
|
}
|
|
|
|
int ret = 2; // normal: in progress
|
|
if (cur_event->code == 0) {
|
|
if (prev_event.code == 0)
|
|
return 0;
|
|
// state change from DTMF back to audio. send DTMF end code.
|
|
ret = 3;
|
|
cur_event = &prev_event;
|
|
}
|
|
else if (prev_event.code == 0)
|
|
ret = 1; // start event
|
|
|
|
int dtmf_code = -1;
|
|
if (cur_event->code >= '0' && cur_event->code <= '9')
|
|
dtmf_code = cur_event->code - '0';
|
|
else if (cur_event->code == '*')
|
|
dtmf_code = 10;
|
|
else if (cur_event->code == '#')
|
|
dtmf_code = 11;
|
|
else if (cur_event->code >= 'A' && cur_event->code <= 'D')
|
|
dtmf_code = cur_event->code - 'A' + 12;
|
|
else {
|
|
ilog(LOG_ERR | LOG_FLAG_LIMIT, "Unknown DTMF event code %i", cur_event->code);
|
|
return 0;
|
|
}
|
|
|
|
// replace audio RTP frame with DTMF payload
|
|
struct telephone_event_payload *ev_pt = (void *) buf->s;
|
|
buf->len = sizeof(*ev_pt);
|
|
ZERO(*ev_pt);
|
|
|
|
ev_pt->event = dtmf_code;
|
|
if (cur_event->volume > 0)
|
|
ev_pt->volume = 0;
|
|
else if (cur_event->volume >= -63)
|
|
ev_pt->volume = -1 * cur_event->volume;
|
|
else
|
|
ev_pt->volume = 63;
|
|
ev_pt->end = (ret == 3) ? 1 : 0;
|
|
ev_pt->duration = htons(*pts - cur_event->ts + duration);
|
|
|
|
// fix up timestamp
|
|
*pts = cur_event->ts;
|
|
|
|
return ret;
|
|
}
|