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

Reply via email to