From d5face6404c604e0baff7aed9a1ba5db18431f51 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sat, 27 Feb 2021 12:07:32 +0100 Subject: Add DTMF detection via telephone-event payload --- src/router/audio.c | 37 +++++++++++++++ src/router/audio.h | 4 ++ src/router/call.c | 135 +++++++++++++++++++++++++++++++++++++++++++---------- src/router/call.h | 13 ++++++ 4 files changed, 164 insertions(+), 25 deletions(-) (limited to 'src/router') diff --git a/src/router/audio.c b/src/router/audio.c index 0c4b6c3..16e83c0 100644 --- a/src/router/audio.c +++ b/src/router/audio.c @@ -127,6 +127,12 @@ void receive_originator(struct osmo_cc_session_codec *codec, uint16_t __attribut len = len / 2; sample_t samples[len]; + if (codec->decoder == decode_te) { + struct telephone_event *te = (struct telephone_event *)data; + telephone_event(relation, te); + return; + } + /* convert int16 to samples */ int16_to_samples(samples, (int16_t *)data, len); @@ -169,6 +175,11 @@ void receive_terminator(struct osmo_cc_session_codec *codec, uint16_t __attribut len = len / 2; sample_t samples[len]; + if (codec->decoder == decode_te) { + PDEBUG(DROUTER, DEBUG_NOTICE, "Ignoring received telephony-events from terminator.\n"); + return; + } + int16_to_samples(samples, (int16_t *)data, len); /* forward to originator, if not a forking call */ @@ -284,3 +295,29 @@ void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len *dst_len = len * 2; } +void encode_te(uint8_t __attribute__((unused)) *src_data, int __attribute__((unused)) src_len, uint8_t **dst_data, int *dst_len) +{ + /* FIXME: TBD... */ + *dst_data = NULL; + *dst_len = 0; +} + +void decode_te(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len) +{ + uint8_t *src = src_data; + struct telephone_event *te; + + if (src_len < 4) + return; + + te = calloc(1, sizeof(*te)); + if (!te) + return; + te->event = src[0]; + te->e = src[1] >> 7; + te->r = (src[1] >> 6) & 0x1; + te->volume = src[1] & 0x3f; + te->duration = (src[2] << 16) | src[3]; + *dst_data = (uint8_t *)te; + *dst_len = sizeof(*te); +} diff --git a/src/router/audio.h b/src/router/audio.h index 390e54f..cf2d34c 100644 --- a/src/router/audio.h +++ b/src/router/audio.h @@ -1,6 +1,10 @@ + void receive_originator(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len); void receive_terminator(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len); void call_media_handle(void); void call_clock(int len); void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len); void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len); +void encode_te(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len); +void decode_te(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len); + diff --git a/src/router/call.c b/src/router/call.c index 5b8c28b..13c2844 100644 --- a/src/router/call.c +++ b/src/router/call.c @@ -81,10 +81,11 @@ struct timer status_timer; static osmo_cc_endpoint_t *cc_ep; static const char *routing_script, *routing_shell; -static struct osmo_cc_helper_audio_codecs codecs[] = { -// { "L16", 8000, 1, encode_l16, decode_l16 }, FIXME: make codecs selectable, if more codecs are supported in the future +static struct osmo_cc_helper_audio_codecs codecs_def[] = { { "PCMA", 8000, 1, g711_encode_alaw, g711_decode_alaw }, { "PCMU", 8000, 1, g711_encode_ulaw, g711_decode_ulaw }, + { "telephone-event", 8000, 1, encode_te, decode_te }, + { "L16", 8000, 1, encode_l16, decode_l16 }, { NULL, 0, 0, NULL, NULL}, }; @@ -386,7 +387,7 @@ static void proxy_send_sdp_answer(call_relation_t *relation, osmo_cc_msg_t *msg) // if (!relation->codec_negotiated || msg->type == OSMO_CC_MSG_SETUP_CNF) { gibt einen crash, da codec vor der antwort schon gesetzt ist. warum sollten wir nach einer antwort denn nochmal den codec schicken? if (!relation->codec_negotiated) { - sdp = osmo_cc_helper_audio_accept(relation, codecs, receive_originator, relation->call->setup_msg, &relation->cc_session, &relation->codec, 0); + sdp = osmo_cc_helper_audio_accept(relation, relation->orig_codecs, receive_originator, relation->call->setup_msg, &relation->cc_session, &relation->codec, 0); if (sdp) { relation->codec_negotiated = 1; PDEBUG(DROUTER, DEBUG_DEBUG, "Sending SDP answer to originator with supported codecs.\n"); @@ -1119,7 +1120,7 @@ void cc_message(osmo_cc_endpoint_t __attribute__((unused)) *ep, uint32_t callref * process message from routing */ -static const char *value_of_param(const char *arg, const char *param, const char **value_p) +static const char *value_of_param(char *arg, char *param, char **value_p) { if (!!strncmp(arg, param, strlen(param))) return NULL; @@ -1140,10 +1141,39 @@ static const char *value_of_param(const char *arg, const char *param, const char return arg; } +static void add_codecs_by_names(struct osmo_cc_helper_audio_codecs *codecs, char *names) +{ + int c = 0, i; + const char *name; + + while ((name = strsep(&names, ","))) { + printf("name='%s'\n", name); + for (i = 0; codecs_def[i].payload_name; i++) { + if (!strcasecmp(name, codecs_def[i].payload_name)) { + if (c == MAX_CODECS) { + PDEBUG(DROUTER, DEBUG_ERROR, "Maximum of %d codecs are reached. Ignoring codec '%s'.\n", c, name); + break; + } + memcpy(&codecs[c], &codecs_def[i], sizeof(*codecs)); + PDEBUG(DROUTER, DEBUG_INFO, "Adding given codec '%s'.\n", codecs[c].payload_name); + c++; + break; + } + } + if (!codecs_def[i].payload_name) { + PDEBUG(DROUTER, DEBUG_ERROR, "Given codec '%s' not supported!\n", name); + } + } + memset(&codecs[c], 0, sizeof(*codecs)); +} + /* routing orders us to activate rtp proxy */ -static void routing_rtp_proxy(call_t *call) +static void routing_rtp_proxy(call_t *call, int argc, char *argv[]) { call_relation_t *relation = call->relation_list; + int orig_given = 0, term_given = 0; + int i; + char *value; /* ignore, if already enabled */ if (relation->rtp_proxy) { @@ -1151,6 +1181,37 @@ static void routing_rtp_proxy(call_t *call) return; } + /* loop through all arguments and stop if there is a ':' */ + for (i = 0; i < argc ; i++) { + if (value_of_param(argv[i], "orig-codecs", &value)) { + PDEBUG(DROUTER, DEBUG_INFO, "Originating codecs given: '%s'.\n", value); + add_codecs_by_names(relation->orig_codecs, value); + if (relation->orig_codecs[0].payload_name) + orig_given = 1; + } else if (value_of_param(argv[i], "term-codecs", &value)) { + PDEBUG(DROUTER, DEBUG_INFO, "Terminating codecs given: '%s'.\n", value); + add_codecs_by_names(relation->term_codecs, value); + if (relation->term_codecs[0].payload_name) + term_given = 1; + } else + PDEBUG(DROUTER, DEBUG_ERROR, "Unknown 'rtp-proxy' parameter '%s' from routing.\n", argv[i]); + } + + if (!orig_given) { + memcpy(&relation->orig_codecs[0], &codecs_def[0], sizeof(relation->orig_codecs[0])); + memcpy(&relation->orig_codecs[1], &codecs_def[1], sizeof(relation->orig_codecs[1])); + memcpy(&relation->orig_codecs[2], &codecs_def[2], sizeof(relation->orig_codecs[2])); + memset(&relation->orig_codecs[3], 0, sizeof(relation->orig_codecs[3])); + PDEBUG(DROUTER, DEBUG_INFO, "No originating codeds given, using default '%s' and '%s' and '%s'.\n", relation->orig_codecs[0].payload_name, relation->orig_codecs[1].payload_name, relation->orig_codecs[2].payload_name); + } + + if (!term_given) { + memcpy(&relation->term_codecs[0], &codecs_def[0], sizeof(relation->term_codecs[0])); + memcpy(&relation->term_codecs[1], &codecs_def[1], sizeof(relation->term_codecs[1])); + memset(&relation->term_codecs[2], 0, sizeof(relation->term_codecs[2])); + PDEBUG(DROUTER, DEBUG_INFO, "No terminating codeds given, using default '%s' and '%s'.\n", relation->term_codecs[0].payload_name, relation->term_codecs[1].payload_name); + } + /* error, if we already negotiated */ if (call->sdp_forwarded) { PDEBUG(DROUTER, DEBUG_ERROR, "RTP-Proxy cannot be enabled now, because we already forwarded a call.\n"); @@ -1167,10 +1228,10 @@ static void routing_rtp_proxy(call_t *call) } /* routing orders us to play a wave file */ -static void routing_play(call_t *call, int argc, const char *argv[]) +static void routing_play(call_t *call, int argc, char *argv[]) { call_relation_t *relation = call->relation_list; - const char *filename = NULL, *volume = "1.0", *loop = NULL; + char *filename = NULL, *volume = "1.0", *loop = NULL; int i; int samplerate = 8000, channels = 0; double deviation; @@ -1228,10 +1289,10 @@ static void routing_play_stop(call_t *call) } /* routing orders us to record a wave file */ -static void routing_record(call_t *call, int argc, const char *argv[]) +static void routing_record(call_t *call, int argc, char *argv[]) { call_relation_t *relation = call->relation_list; - const char *filename = NULL, *volume = "1.0"; + char *filename = NULL, *volume = "1.0"; int i; int samplerate = 8000, channels = 2; int rc; @@ -1274,10 +1335,10 @@ static void routing_record_stop(call_t *call) } /* routing orders us to set local gain */ -static void routing_gain(call_t *call, int argc, const char *argv[], int tx) +static void routing_gain(call_t *call, int argc, char *argv[], int tx) { int i; - const char *gain = NULL; + char *gain = NULL; if (!call->relation_list->rtp_proxy) { PDEBUG(DROUTER, DEBUG_ERROR, "RTP-Proxy must be enabled to record a file!\n"); @@ -1306,15 +1367,15 @@ static void routing_gain(call_t *call, int argc, const char *argv[], int tx) } /* routing orders us to call remote end */ -static void routing_call(call_t *call, int argc, const char *argv[]) +static void routing_call(call_t *call, int argc, char *argv[]) { - const char *interface; - const char *bearer_coding, *bearer_capability, *bearer_mode; - const char *calling, *calling_type, *calling_plan, *calling_present, *calling_screen, *no_calling; - const char *calling2, *calling2_type, *calling2_plan, *calling2_present, *calling2_screen, *no_calling2; - const char *redirecting, *redirecting_type, *redirecting_plan, *redirecting_present, *redirecting_screen, *redirecting_reason, *no_redirecting; - const char *dialing, *dialing_type, *dialing_plan; - const char *keypad; + char *interface; + char *bearer_coding, *bearer_capability, *bearer_mode; + char *calling, *calling_type, *calling_plan, *calling_present, *calling_screen, *no_calling; + char *calling2, *calling2_type, *calling2_plan, *calling2_present, *calling2_screen, *no_calling2; + char *redirecting, *redirecting_type, *redirecting_plan, *redirecting_present, *redirecting_screen, *redirecting_reason, *no_redirecting; + char *dialing, *dialing_type, *dialing_plan; + char *keypad; uint8_t coding, capability, mode; uint8_t type, plan, present, screen, reason; char number[256]; @@ -1528,7 +1589,7 @@ next_call: /* only if RTP-Proxy is used */ if (call->relation_list->rtp_proxy) { PDEBUG(DROUTER, DEBUG_DEBUG, "Sending our codecs to the terminator.\n"); - relation->cc_session = osmo_cc_helper_audio_offer(relation, codecs, receive_terminator, new_msg, 1); + relation->cc_session = osmo_cc_helper_audio_offer(relation, call->relation_list->term_codecs, receive_terminator, new_msg, 1); } else /* sdp from originator's setup message */ if (call->relation_list->sdp) @@ -1666,7 +1727,7 @@ static void routing_answer(call_t *call) } /* routing orders us to dsiconnect/release the call */ -static void routing_disc_rel(call_t *call, int argc, const char *argv[], int disconnect) +static void routing_disc_rel(call_t *call, int argc, char *argv[], int disconnect) { call_relation_t *relation = call->relation_list; uint8_t cause = 0; @@ -1746,7 +1807,7 @@ static void routing_dtmf_stop(call_t *call) } /* routing failed, release the call */ -static void routing_error(call_t *call, const char *error) +static void routing_error(call_t *call, char *error) { osmo_cc_msg_t *new_msg; call_relation_t *relation; @@ -1768,7 +1829,7 @@ static void routing_error(call_t *call, const char *error) #if 0 /* routing script says something */ -static void routing_say(call_t *call, const char *error) +static void routing_say(call_t *call, char *error) { char text[1024] = ""; fuer alle argv @@ -1782,7 +1843,7 @@ void routing_receive_stdout(routing_t *routing, const char *string) { call_t *call = routing->call; int argc = 0; - const char *argv[256], *token; + char *argv[256], *token; /* convert string into tokens */ while ((token = osmo_cc_strtok_quotes(&string))) @@ -1791,7 +1852,7 @@ void routing_receive_stdout(routing_t *routing, const char *string) return; if (!strcasecmp(argv[0], "rtp-proxy")) - routing_rtp_proxy(call); + routing_rtp_proxy(call, argc - 1, argv + 1); else if (!strcasecmp(argv[0], "play")) routing_play(call, argc - 1, argv + 1); @@ -1899,6 +1960,30 @@ void routing_close(routing_t *routing) call_destroy(call); } +void telephone_event(call_relation_t *relation, struct telephone_event *te) +{ + char digit_string[7] = "dtmf x"; + + if (te->event < 16) { + if (!relation->te_started && !te->e && te->volume <= 36) { + PDEBUG(DROUTER, DEBUG_INFO, "Received start of Telephone-Event '%d'\n", te->event); + relation->te_started = 1; + digit_string[5] = "0123456789*#ABCD"[te->event]; + } + if (relation->te_started && te->e) { + PDEBUG(DROUTER, DEBUG_INFO, "Received end of Telephone-Event '%d'\n", te->event); + relation->te_started = 0; + } + } else + PDEBUG(DROUTER, DEBUG_INFO, "Received unsupported Telephone-Event '%d'\n", te->event); + + if (!relation->call->routing.routing) + return; + + if (digit_string[5] != 'x') + routing_send(&relation->call->routing, digit_string); +} + #warning add progress, if terminating call sends sdp but call state already reached #warning beim disc muss progress geprueft werden und damit entschieden ob wir audio mitsenden sollen diff --git a/src/router/call.h b/src/router/call.h index 580f267..e782c26 100644 --- a/src/router/call.h +++ b/src/router/call.h @@ -21,6 +21,8 @@ enum call_state { #include "routing.h" +#define MAX_CODECS 8 + /* relation to upper layer */ typedef struct call_relation { struct call_relation *next; @@ -39,6 +41,8 @@ typedef struct call_relation { int rtp_proxy; osmo_cc_session_t *cc_session; int codec_negotiated; + struct osmo_cc_helper_audio_codecs orig_codecs[MAX_CODECS + 1]; /* codecs for originator */ + struct osmo_cc_helper_audio_codecs term_codecs[MAX_CODECS + 1]; /* codecs for terminator, stored at relation of originator */ osmo_cc_session_codec_t *codec; wave_play_t play; /* play a wave file */ @@ -48,6 +52,7 @@ typedef struct call_relation { wave_rec_t rec; /* record a wave file */ dtmf_dec_t dtmf_dec; /* dtmf decoder */ int dtmf_dec_enable;/* feed decoder with data */ + int te_started; /* we got a digit via telephone-event */ } call_relation_t; /* call instance */ @@ -77,8 +82,16 @@ typedef struct call { extern call_t *call_list; +struct telephone_event { + uint8_t event; + uint8_t e, r; + uint8_t volume; + uint16_t duration; +}; + int call_init(osmo_cc_endpoint_t *ep, const char *_routing_script, const char *_routing_shell); void call_exit(void); int call_handle(void); void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg); +void telephone_event(call_relation_t *relation, struct telephone_event *te); -- cgit v1.2.3