support for sending DTMF via RTP payload (RFC4733)

sayer/1.4-spce2.6
Stefan Sayer 16 years ago
parent 1fd9c544f6
commit 184268f89d

@ -69,6 +69,7 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) {
DEF_CMD("unmute", SCUnmuteAction);
DEF_CMD("enableDTMFDetection", SCEnableDTMFDetection);
DEF_CMD("disableDTMFDetection", SCDisableDTMFDetection);
DEF_CMD("sendDTMF", SCSendDTMFAction);
DEF_CMD("set", SCSetAction);
DEF_CMD("sets", SCSetSAction);
@ -1160,3 +1161,27 @@ EXEC_ACTION_START(SCB2BClearHeadersAction) {
DBG("clearing B2B headers\n");
sc_sess->B2BclearHeaders();
} EXEC_ACTION_END;
CONST_ACTION_2P(SCSendDTMFAction,',', true);
EXEC_ACTION_START(SCSendDTMFAction) {
string event = resolveVars(par1, sess, sc_sess, event_params);
string duration = resolveVars(par2, sess, sc_sess, event_params);
unsigned int event_i;
if (str2i(event, event_i)) {
ERROR("event '%s' not a valid DTMF event\n", event.c_str());
throw DSMException("core", "cause", "invalid DTMF:"+ event);
}
unsigned int duration_i;
if (duration.empty()) {
duration_i = 500; // default
} else {
if (str2i(duration, duration_i)) {
ERROR("event duration '%s' not a valid DTMF duration\n", duration.c_str());
throw DSMException("core", "cause", "invalid DTMF duration:"+ duration);
}
}
sess->sendDtmf(event_i, duration_i);
} EXEC_ACTION_END;

@ -62,6 +62,7 @@ DEF_ACTION_1P(SCMuteAction);
DEF_ACTION_1P(SCUnmuteAction);
DEF_ACTION_1P(SCEnableDTMFDetection);
DEF_ACTION_1P(SCDisableDTMFDetection);
DEF_ACTION_2P(SCSendDTMFAction);
DEF_ACTION_1P(SCSetPromptsAction);
DEF_ACTION_2P(SCAddSeparatorAction);

@ -188,19 +188,87 @@ int AmRtpStream::ping()
return 2;
}
int AmRtpStream::send( unsigned int ts, unsigned char* buffer, unsigned int size )
{
if ((mute) || (hold))
return 0;
void AmRtpStream::sendDtmfPacket(unsigned int ts) {
while (true) {
switch(dtmf_sending_state) {
case DTMF_SEND_NONE: {
dtmf_send_queue_mut.lock();
if (dtmf_send_queue.empty()) {
dtmf_send_queue_mut.unlock();
return;
}
current_send_dtmf = dtmf_send_queue.front();
current_send_dtmf_ts = ts;
dtmf_send_queue.pop();
dtmf_send_queue_mut.unlock();
dtmf_sending_state = DTMF_SEND_SENDING;
current_send_dtmf_ts = ts;
DBG("starting to send DTMF\n");
} break;
case DTMF_SEND_SENDING: {
if (ts_less()(ts, current_send_dtmf_ts + current_send_dtmf.second)) {
// send packet
if (!telephone_event_pt.get())
return;
dtmf_payload_t dtmf;
dtmf.event = current_send_dtmf.first;
dtmf.e = dtmf.r = 0;
dtmf.duration = htons(ts - current_send_dtmf_ts);
DBG("sending DTMF: event=%i; e=%i; r=%i; volume=%i; duration=%i; ts=%u\n",
dtmf.event,dtmf.e,dtmf.r,dtmf.volume,ntohs(dtmf.duration),current_send_dtmf_ts);
compile_and_send(telephone_event_pt->payload_type, dtmf.duration == 0,
current_send_dtmf_ts,
(unsigned char*)&dtmf, sizeof(dtmf_payload_t));
return;
} else {
DBG("ending DTMF\n");
dtmf_sending_state = DTMF_SEND_ENDING;
send_dtmf_end_repeat = 0;
}
} break;
case DTMF_SEND_ENDING: {
if (send_dtmf_end_repeat >= 3) {
DBG("DTMF send complete\n");
dtmf_sending_state = DTMF_SEND_NONE;
} else {
send_dtmf_end_repeat++;
// send packet with end bit set, duration = event duration
if (!telephone_event_pt.get())
return;
dtmf_payload_t dtmf;
dtmf.event = current_send_dtmf.first;
dtmf.e = 1;
dtmf.r = 0;
dtmf.duration = htons(current_send_dtmf.second);
DBG("sending DTMF: event=%i; e=%i; r=%i; volume=%i; duration=%i; ts=%u\n",
dtmf.event,dtmf.e,dtmf.r,dtmf.volume,ntohs(dtmf.duration),current_send_dtmf_ts);
compile_and_send(telephone_event_pt->payload_type, false,
current_send_dtmf_ts,
(unsigned char*)&dtmf, sizeof(dtmf_payload_t));
return;
}
} break;
};
}
if(!size)
return -1;
}
int AmRtpStream::compile_and_send(const int payload, bool marker, unsigned int ts,
unsigned char* buffer, unsigned int size) {
AmRtpPacket rp;
rp.payload = payload;
rp.marker = false;
rp.timestamp = ts;
rp.marker = marker;
rp.sequence = sequence++;
rp.timestamp = ts;
rp.ssrc = l_ssrc;
rp.compile((unsigned char*)buffer,size);
@ -243,6 +311,20 @@ int AmRtpStream::send( unsigned int ts, unsigned char* buffer, unsigned int size
return size;
}
int AmRtpStream::send( unsigned int ts, unsigned char* buffer, unsigned int size )
{
if ((mute) || (hold))
return 0;
sendDtmfPacket(ts);
if(!size)
return -1;
return compile_and_send(payload, false, ts, buffer, size);
}
int AmRtpStream::send_raw( char* packet, unsigned int length )
{
if ((mute) || (hold))
@ -351,7 +433,8 @@ AmRtpStream::AmRtpStream(AmSession* _s)
mute(false),
hold(false),
receiving(true),
monitor_rtp_timeout(true)
monitor_rtp_timeout(true),
dtmf_sending_state(DTMF_SEND_NONE)
{
#ifdef SUPPORT_IPV6
@ -625,6 +708,14 @@ int AmRtpStream::getTelephoneEventRate()
return retval;
}
void AmRtpStream::sendDtmf(int event, unsigned int duration_ms) {
dtmf_send_queue_mut.lock();
dtmf_send_queue.push(std::make_pair(event, duration_ms * getTelephoneEventRate()
/ 1000));
dtmf_send_queue_mut.unlock();
DBG("enqueued DTMF event %i duration %u\n", event, duration_ms);
}
PacketMem::PacketMem() {
memset(used, 0, sizeof(used));
}

@ -43,6 +43,7 @@
#include <memory>
using std::string;
using std::auto_ptr;
using std::pair;
// return values of AmRtpStream::receive
#define RTP_EMPTY 0 // no rtp packet available
@ -141,6 +142,23 @@ protected:
AmSession* session;
int compile_and_send(const int payload, bool marker,
unsigned int ts, unsigned char* buffer,
unsigned int size);
void sendDtmfPacket(unsigned int ts);
// event, duration
std::queue<pair<int, unsigned int> > dtmf_send_queue;
AmMutex dtmf_send_queue_mut;
enum dtmf_sending_state_t {
DTMF_SEND_NONE, // not sending event
DTMF_SEND_SENDING, // sending event
DTMF_SEND_ENDING // sending end of event
} dtmf_sending_state;
pair<int, unsigned int> current_send_dtmf;
unsigned int current_send_dtmf_ts;
int send_dtmf_end_repeat;
public:
@ -163,7 +181,7 @@ public:
int send( unsigned int ts,
unsigned char* buffer,
unsigned int size );
int send_raw( char* packet, unsigned int length );
int receive( unsigned char* buffer, unsigned int size,
@ -226,6 +244,13 @@ public:
int getTelephoneEventRate();
/**
* send a DTMF as RTP payload (RFC4733)
* @param event event ID (e.g. key press), see rfc
* @param duration_ms duration in milliseconds
*/
void sendDtmf(int event, unsigned int duration_ms);
/**
* Enables RTP stream.
* @param sdp_payload payload from the SDP message.

@ -584,6 +584,11 @@ void AmSession::putDtmfAudio(const unsigned char *buf, int size, int user_ts)
m_dtmfEventQueue.putDtmfAudio(buf, size, user_ts);
}
void AmSession::sendDtmf(int event, unsigned int duration_ms) {
RTPStream()->sendDtmf(event, duration_ms);
}
void AmSession::onDtmf(int event, int duration_msec)
{
DBG("AmSession::onDtmf(%i,%i)\n",event,duration_msec);

@ -384,6 +384,14 @@ public:
bool isDtmfDetectionEnabled() { return m_dtmfDetectionEnabled; }
void setDtmfDetectionEnabled(bool e) { m_dtmfDetectionEnabled = e; }
void putDtmfAudio(const unsigned char *buf, int size, int user_ts);
/**
* send a DTMF as RTP payload (RFC4733)
* @param event event ID (e.g. key press), see rfc
* @param duration_ms duration in milliseconds
*/
void sendDtmf(int event, unsigned int duration_ms);
/** event handler for apps to use*/
virtual void onDtmf(int event, int duration);

@ -91,6 +91,10 @@ core actions:
enableDTMFDetection()
disableDTMFDetection()
sendDTMF(key [, duration_ms])
send a DTMF event (RFC4733 event)
duration_ms defaults to 500ms
B2B.connectCallee(remote_party, remote_uri)
connect second leg of B2B session (see AmB2BSession)

Loading…
Cancel
Save