Module: sems Branch: master Commit: 184268f89d2b8d37d142def1998fd228acf0ca81 URL: http://git.sip-router.org/cgi-bin/gitweb.cgi/sems/?a=commit;h=184268f89d2b8d37d142def1998fd228acf0ca81
Author: Stefan Sayer <[email protected]> Committer: Stefan Sayer <[email protected]> Date: Mon Jul 5 20:11:42 2010 +0200 support for sending DTMF via RTP payload (RFC4733) --- apps/dsm/DSMCoreModule.cpp | 25 ++++++++++ apps/dsm/DSMCoreModule.h | 1 + core/AmRtpStream.cpp | 109 ++++++++++++++++++++++++++++++++++++++++---- core/AmRtpStream.h | 27 ++++++++++- core/AmSession.cpp | 5 ++ core/AmSession.h | 8 +++ doc/dsm/dsm_syntax.txt | 4 ++ 7 files changed, 169 insertions(+), 10 deletions(-) diff --git a/apps/dsm/DSMCoreModule.cpp b/apps/dsm/DSMCoreModule.cpp index e8d98aa..3cf69f3 100644 --- a/apps/dsm/DSMCoreModule.cpp +++ b/apps/dsm/DSMCoreModule.cpp @@ -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; diff --git a/apps/dsm/DSMCoreModule.h b/apps/dsm/DSMCoreModule.h index b4f6b41..5c45ebc 100644 --- a/apps/dsm/DSMCoreModule.h +++ b/apps/dsm/DSMCoreModule.h @@ -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); diff --git a/core/AmRtpStream.cpp b/core/AmRtpStream.cpp index 6951843..0d41b61 100644 --- a/core/AmRtpStream.cpp +++ b/core/AmRtpStream.cpp @@ -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)); } diff --git a/core/AmRtpStream.h b/core/AmRtpStream.h index 685ca74..a5cad63 100644 --- a/core/AmRtpStream.h +++ b/core/AmRtpStream.h @@ -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, @@ -227,6 +245,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. * @warning start() must have been called so that play and record work. diff --git a/core/AmSession.cpp b/core/AmSession.cpp index 867e5ce..d622dab 100644 --- a/core/AmSession.cpp +++ b/core/AmSession.cpp @@ -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); diff --git a/core/AmSession.h b/core/AmSession.h index f143185..c0694e9 100644 --- a/core/AmSession.h +++ b/core/AmSession.h @@ -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); diff --git a/doc/dsm/dsm_syntax.txt b/doc/dsm/dsm_syntax.txt index 7e03dd9..1450247 100644 --- a/doc/dsm/dsm_syntax.txt +++ b/doc/dsm/dsm_syntax.txt @@ -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) _______________________________________________ Semsdev mailing list [email protected] http://lists.iptel.org/mailman/listinfo/semsdev
