Add support for telephone events
Not much tested yet, except for sending telephone events via SIP.
This commit is contained in:
parent
25a675a4ce
commit
a138935937
|
@ -139,7 +139,7 @@ void receive_originator(struct osmo_cc_session_codec *codec, uint8_t marker, uin
|
|||
|
||||
if (codec->decoder == decode_te) {
|
||||
struct telephone_event *te = (struct telephone_event *)data;
|
||||
telephone_event(relation, marker, te);
|
||||
rx_telephone_event(relation, marker, te);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -204,6 +204,20 @@ void receive_terminator(struct osmo_cc_session_codec *codec, uint8_t __attribute
|
|||
jitter_save(&call->term_dejitter, samples, len, 1, sequence, timestamp, ssrc);
|
||||
}
|
||||
|
||||
void tx_telephone_event(call_relation_t *relation, uint8_t marker, struct telephone_event *te)
|
||||
{
|
||||
/* only if codec was negotiated */
|
||||
if (!relation->telephone_event)
|
||||
return;
|
||||
|
||||
/* there should be a codec at this point; just to be safe */
|
||||
if (!relation->codec)
|
||||
return;
|
||||
|
||||
/* encode and send via RTP */
|
||||
osmo_cc_rtp_send_ts(relation->telephone_event, (uint8_t *)te, sizeof(*te), marker, relation->codec->media->tx_sequence, relation->codec->media->tx_timestamp, relation);
|
||||
}
|
||||
|
||||
void call_clock(int len)
|
||||
{
|
||||
call_t *call;
|
||||
|
@ -303,9 +317,23 @@ void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len
|
|||
|
||||
void encode_te(uint8_t __attribute__((unused)) *src_data, int __attribute__((unused)) src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
/* FIXME: TBD... */
|
||||
*dst_data = NULL;
|
||||
*dst_len = 0;
|
||||
struct telephone_event *te = (void *)src_data;
|
||||
uint8_t *dst;
|
||||
|
||||
if (src_len != sizeof(*te))
|
||||
return;
|
||||
|
||||
dst = calloc(1, 4);
|
||||
if (!dst)
|
||||
return;
|
||||
dst[0] = te->event;
|
||||
dst[1] = te->e << 7;
|
||||
dst[1] |= te->r << 6;
|
||||
dst[1] |= (te->volume < 0x3f) ? te->volume : 0x3f;
|
||||
dst[2] |= te->duration >> 8;
|
||||
dst[3] |= te->duration;
|
||||
*dst_data = dst;
|
||||
*dst_len = 4;
|
||||
}
|
||||
|
||||
void decode_te(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
|
@ -323,7 +351,8 @@ void decode_te(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len,
|
|||
te->e = src[1] >> 7;
|
||||
te->r = (src[1] >> 6) & 0x1;
|
||||
te->volume = src[1] & 0x3f;
|
||||
te->duration = (src[2] << 16) | src[3];
|
||||
te->duration = (src[2] << 8) | src[3];
|
||||
*dst_data = (uint8_t *)te;
|
||||
*dst_len = sizeof(*te);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
void audio_init(void);
|
||||
void receive_originator(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
|
||||
void receive_terminator(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
|
||||
void tx_telephone_event(call_relation_t *relation, uint8_t marker, struct telephone_event *te);
|
||||
void call_clock(int len);
|
||||
void encode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void decode_l16(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
|
|
|
@ -412,7 +412,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->cc_ep->session_config, relation, relation->orig_codecs, receive_originator, relation->call->setup_msg, &relation->cc_session, &relation->codec, 0);
|
||||
sdp = osmo_cc_helper_audio_accept_te(&relation->cc_ep->session_config, relation, relation->orig_codecs, receive_originator, relation->call->setup_msg, &relation->cc_session, &relation->codec, &relation->telephone_event, 0);
|
||||
if (sdp) {
|
||||
relation->codec_negotiated = 1;
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Sending SDP answer to originator with supported codecs.\n");
|
||||
|
@ -1110,7 +1110,7 @@ void cc_message(osmo_cc_endpoint_t *cc_ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
/* remove progress, since it will be added with the SDP answer */
|
||||
osmo_cc_remove_ie(msg, OSMO_CC_IE_PROGRESS, 0);
|
||||
/* negotiate codec */
|
||||
osmo_cc_helper_audio_negotiate(msg, &relation->cc_session, &relation->codec);
|
||||
osmo_cc_helper_audio_negotiate_te(msg, &relation->cc_session, &relation->codec, &relation->telephone_event);
|
||||
if (relation->codec)
|
||||
relation->codec_negotiated = 1;
|
||||
}
|
||||
|
@ -1184,6 +1184,9 @@ static struct param {
|
|||
/* disc/rel */
|
||||
int isdn_cause, sip_cause;
|
||||
|
||||
/* telephone-event */
|
||||
char *event;
|
||||
|
||||
/* error */
|
||||
char *error;
|
||||
} param;
|
||||
|
@ -1344,8 +1347,9 @@ static void routing_rtp_proxy(call_t *call, int argc, char *argv[], struct comma
|
|||
if (!param.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);
|
||||
memcpy(&relation->term_codecs[2], &codecs_def[2], sizeof(relation->term_codecs[2]));
|
||||
memset(&relation->term_codecs[3], 0, sizeof(relation->term_codecs[3]));
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "No terminating codeds given, using default '%s' and '%s' and '%s'.\n", relation->term_codecs[0].payload_name, relation->term_codecs[1].payload_name, relation->term_codecs[2].payload_name);
|
||||
} else
|
||||
memcpy(relation->term_codecs, param.term_codecs, sizeof(param.term_codecs));
|
||||
|
||||
|
@ -1964,13 +1968,16 @@ static void routing_release(call_t *call, int argc, char *argv[], struct command
|
|||
void recv_dtmf(void *priv, char digit, dtmf_meas_t __attribute__((unused)) *meas)
|
||||
{
|
||||
call_relation_t *relation = (call_relation_t *)priv;
|
||||
char digit_string[7] = "dtmf x";
|
||||
char digit_string[7+6+1] = "called-dtmf x";
|
||||
|
||||
if (!relation->call->routing.routing)
|
||||
return;
|
||||
|
||||
digit_string[5] = digit;
|
||||
routing_send(&relation->call->routing, digit_string);
|
||||
digit_string[7+5] = digit;
|
||||
if (relation == relation->call->relation_list)
|
||||
routing_send(&relation->call->routing, digit_string + 7);
|
||||
else
|
||||
routing_send(&relation->call->routing, digit_string);
|
||||
}
|
||||
|
||||
/* routing orders us to enable DTMF decoding */
|
||||
|
@ -1978,6 +1985,15 @@ static void routing_dtmf(call_t *call, int __attribute__((unused)) argc, char __
|
|||
{
|
||||
call_relation_t *relation = call->relation_list;
|
||||
|
||||
if (!strcmp(command_def->name, "called-dtmf")) {
|
||||
relation = relation->next;
|
||||
|
||||
if (!relation || relation->next) {
|
||||
PDEBUG(DROUTER, DEBUG_NOTICE, "Cannot enable DTMF detection on the called side, because there is no single terminating callee.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dtmf_decode_exit(&relation->dtmf_dec);
|
||||
|
||||
/* we add 13, because we working at speech level and not at 0dBm, which is -13 dBm */
|
||||
|
@ -1991,11 +2007,97 @@ static void routing_dtmf_stop(call_t *call, int __attribute__((unused)) argc, ch
|
|||
{
|
||||
call_relation_t *relation = call->relation_list;
|
||||
|
||||
if (!strcmp(command_def->name, "called-dtmf-stop")) {
|
||||
relation = relation->next;
|
||||
|
||||
if (!relation || relation->next)
|
||||
return;
|
||||
}
|
||||
dtmf_decode_exit(&relation->dtmf_dec);
|
||||
|
||||
relation->dtmf_dec_enable = 0;
|
||||
}
|
||||
|
||||
/* routing orders us to send a telephone event */
|
||||
struct param_def param_event[] = {
|
||||
{ .n = "event", .s = ¶m.event, .d = "0..9|*|#|A..D", .mandatory = 1 },
|
||||
{ .n = NULL }
|
||||
};
|
||||
|
||||
static void routing_te(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def)
|
||||
{
|
||||
call_relation_t *relation = call->relation_list;
|
||||
struct telephone_event te;
|
||||
uint8_t marker;
|
||||
uint8_t event;
|
||||
|
||||
if (!strcmp(command_def->name, "called-telephone-event")) {
|
||||
relation = relation->next;
|
||||
|
||||
if (!relation || relation->next) {
|
||||
PDEBUG(DROUTER, DEBUG_NOTICE, "Cannot send telephone event, because there is no single terminating callee.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!relation->telephone_event) {
|
||||
PDEBUG(DROUTER, DEBUG_NOTICE, "Cannot send telephone event, because the codec was not negotiated.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
parse_params(argc, argv, command_def);
|
||||
|
||||
if (!param.event || !param.event[0] || param.event[1]) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'telephone-event' command reqires an event character as parameter.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (param.event[0]) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
event = param.event[0] - '0';
|
||||
break;
|
||||
case '*':
|
||||
event = 10;
|
||||
break;
|
||||
case '#':
|
||||
event = 11;
|
||||
break;
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
event = param.event[0] - 'a' + 12;
|
||||
break;
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
event = param.event[0] - 'A' + 12;
|
||||
break;
|
||||
default:
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'telephone-event' with illegal event character '%c'.\n", param.event[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&te, 0, sizeof(te));
|
||||
marker = 1;
|
||||
te.event = event;
|
||||
te.e = 1;
|
||||
te.volume = 0;
|
||||
te.duration = 800; /* 100 ms */
|
||||
|
||||
tx_telephone_event(relation, marker, &te);
|
||||
}
|
||||
|
||||
/* routing failed, release the call */
|
||||
struct param_def param_error[] = {
|
||||
{ .n = "string", .s = ¶m.error, .d = "error string to be displayed", .mandatory = 1 },
|
||||
|
@ -2049,6 +2151,10 @@ struct command_def command_def[] = {
|
|||
{ "release", routing_release, "Release call towards caller and release callee, if any.", param_disc_rel },
|
||||
{ "dtmf", routing_dtmf, "Turn on DTMF detection.", NULL },
|
||||
{ "dtmf-stop", routing_dtmf_stop, "Turn off DTMF detection.", NULL },
|
||||
{ "called-dtmf", routing_dtmf, "Turn on DTMF detection at callee.", NULL },
|
||||
{ "called-dtmf-stop", routing_dtmf_stop, "Turn off DTMF detection at callee.", NULL },
|
||||
{ "telephone-event", routing_te, "Send digit as telephone event to caller.", param_event },
|
||||
{ "called-telephone-event", routing_te, "Send digit as telephone event to callee.", param_event },
|
||||
{ "error", routing_error, "Display error message.", param_error },
|
||||
{ NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
@ -2060,6 +2166,11 @@ void routing_receive_stdout(routing_t *routing, const char *string)
|
|||
char *argv[256], *token;
|
||||
int i;
|
||||
|
||||
if (routing->call->relation_list)
|
||||
PDEBUG(DSTDERR, DEBUG_INFO, "(call #%d) Command from routing: '%s'\n", routing->call->num, string);
|
||||
else
|
||||
PDEBUG(DSTDERR, DEBUG_INFO, "Command from routing: '%s'\n", string);
|
||||
|
||||
/* convert string into tokens */
|
||||
while ((token = osmo_cc_strtok_quotes(&string)))
|
||||
argv[argc++] = strdup(token);
|
||||
|
@ -2123,7 +2234,7 @@ void routing_close(routing_t *routing)
|
|||
call_destroy(call);
|
||||
}
|
||||
|
||||
void telephone_event(call_relation_t *relation, uint8_t marker, struct telephone_event *te)
|
||||
void rx_telephone_event(call_relation_t *relation, uint8_t marker, struct telephone_event *te)
|
||||
{
|
||||
char digit_string[7] = "dtmf x";
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ typedef struct call_relation {
|
|||
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;
|
||||
osmo_cc_session_codec_t *codec, *telephone_event; /* codec and optional telephone event codec */
|
||||
|
||||
dtmf_dec_t dtmf_dec; /* dtmf decoder */
|
||||
int dtmf_dec_enable;/* feed decoder with data */
|
||||
|
@ -105,7 +105,7 @@ int call_init(osmo_cc_endpoint_t *cc_ep1, osmo_cc_endpoint_t *cc_ep2, const char
|
|||
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, uint8_t marker, struct telephone_event *te);
|
||||
void rx_telephone_event(call_relation_t *relation, uint8_t marker, struct telephone_event *te);
|
||||
|
||||
void routing_help(void);
|
||||
|
||||
|
|
Loading…
Reference in New Issue