Browse code

support for sending DTMF via RTP payload (RFC4733)

Stefan Sayer authored on 05/07/2010 18:11:42
Showing 7 changed files
... ...
@@ -69,6 +69,7 @@ DSMAction* DSMCoreModule::getAction(const string& from_str) {
69 69
   DEF_CMD("unmute", SCUnmuteAction);
70 70
   DEF_CMD("enableDTMFDetection", SCEnableDTMFDetection);
71 71
   DEF_CMD("disableDTMFDetection", SCDisableDTMFDetection);
72
+  DEF_CMD("sendDTMF", SCSendDTMFAction);
72 73
 
73 74
   DEF_CMD("set", SCSetAction);
74 75
   DEF_CMD("sets", SCSetSAction);
... ...
@@ -1160,3 +1161,27 @@ EXEC_ACTION_START(SCB2BClearHeadersAction) {
1160 1161
   DBG("clearing B2B headers\n");
1161 1162
   sc_sess->B2BclearHeaders();
1162 1163
 } EXEC_ACTION_END;
1164
+
1165
+CONST_ACTION_2P(SCSendDTMFAction,',', true);
1166
+EXEC_ACTION_START(SCSendDTMFAction) {
1167
+  string event = resolveVars(par1, sess, sc_sess, event_params);
1168
+  string duration = resolveVars(par2, sess, sc_sess, event_params);  
1169
+  
1170
+  unsigned int event_i;
1171
+  if (str2i(event, event_i)) {
1172
+    ERROR("event '%s' not a valid DTMF event\n", event.c_str());
1173
+    throw DSMException("core", "cause", "invalid DTMF:"+ event);
1174
+  }
1175
+
1176
+  unsigned int duration_i;
1177
+  if (duration.empty()) {
1178
+    duration_i = 500; // default
1179
+  } else {
1180
+    if (str2i(duration, duration_i)) {
1181
+      ERROR("event duration '%s' not a valid DTMF duration\n", duration.c_str());
1182
+      throw DSMException("core", "cause", "invalid DTMF duration:"+ duration);
1183
+    }
1184
+  }
1185
+
1186
+  sess->sendDtmf(event_i, duration_i);
1187
+} EXEC_ACTION_END;
... ...
@@ -62,6 +62,7 @@ DEF_ACTION_1P(SCMuteAction);
62 62
 DEF_ACTION_1P(SCUnmuteAction);
63 63
 DEF_ACTION_1P(SCEnableDTMFDetection);
64 64
 DEF_ACTION_1P(SCDisableDTMFDetection);
65
+DEF_ACTION_2P(SCSendDTMFAction);
65 66
 
66 67
 DEF_ACTION_1P(SCSetPromptsAction);
67 68
 DEF_ACTION_2P(SCAddSeparatorAction);
... ...
@@ -188,19 +188,87 @@ int AmRtpStream::ping()
188 188
   return 2;
189 189
 }
190 190
 
191
-int AmRtpStream::send( unsigned int ts, unsigned char* buffer, unsigned int size )
192
-{
193
-  if ((mute) || (hold))
194
-    return 0;
191
+void AmRtpStream::sendDtmfPacket(unsigned int ts) {
192
+  while (true) {
193
+    switch(dtmf_sending_state) {
194
+    case DTMF_SEND_NONE: {
195
+      dtmf_send_queue_mut.lock();
196
+      if (dtmf_send_queue.empty()) {
197
+	dtmf_send_queue_mut.unlock();
198
+	return;
199
+      }
200
+      current_send_dtmf = dtmf_send_queue.front();
201
+      current_send_dtmf_ts = ts;
202
+      dtmf_send_queue.pop();
203
+      dtmf_send_queue_mut.unlock();
204
+      dtmf_sending_state = DTMF_SEND_SENDING;
205
+      current_send_dtmf_ts = ts;
206
+      DBG("starting to send DTMF\n");
207
+    } break;
208
+      
209
+    case DTMF_SEND_SENDING: {
210
+      if (ts_less()(ts, current_send_dtmf_ts + current_send_dtmf.second)) {
211
+	// send packet
212
+	if (!telephone_event_pt.get()) 
213
+	  return;
214
+
215
+	dtmf_payload_t dtmf;
216
+	dtmf.event = current_send_dtmf.first;
217
+	dtmf.e = dtmf.r = 0;
218
+	dtmf.duration = htons(ts - current_send_dtmf_ts);
219
+
220
+	DBG("sending DTMF: event=%i; e=%i; r=%i; volume=%i; duration=%i; ts=%u\n",
221
+	    dtmf.event,dtmf.e,dtmf.r,dtmf.volume,ntohs(dtmf.duration),current_send_dtmf_ts);
222
+
223
+	compile_and_send(telephone_event_pt->payload_type, dtmf.duration == 0, 
224
+			 current_send_dtmf_ts, 
225
+			 (unsigned char*)&dtmf, sizeof(dtmf_payload_t)); 
226
+	return;
227
+
228
+      } else {
229
+	DBG("ending DTMF\n");
230
+	dtmf_sending_state = DTMF_SEND_ENDING;
231
+	send_dtmf_end_repeat = 0;
232
+      }
233
+    } break;
234
+      
235
+    case DTMF_SEND_ENDING:  {
236
+      if (send_dtmf_end_repeat >= 3) {
237
+	DBG("DTMF send complete\n");
238
+	dtmf_sending_state = DTMF_SEND_NONE;
239
+      } else {
240
+	send_dtmf_end_repeat++;
241
+	// send packet with end bit set, duration = event duration
242
+	if (!telephone_event_pt.get()) 
243
+	  return;
244
+
245
+	dtmf_payload_t dtmf;
246
+	dtmf.event = current_send_dtmf.first;
247
+	dtmf.e = 1; 
248
+	dtmf.r = 0;
249
+	dtmf.duration = htons(current_send_dtmf.second);
250
+
251
+	DBG("sending DTMF: event=%i; e=%i; r=%i; volume=%i; duration=%i; ts=%u\n",
252
+	    dtmf.event,dtmf.e,dtmf.r,dtmf.volume,ntohs(dtmf.duration),current_send_dtmf_ts);
253
+
254
+	compile_and_send(telephone_event_pt->payload_type, false, 
255
+			 current_send_dtmf_ts, 
256
+			 (unsigned char*)&dtmf, sizeof(dtmf_payload_t)); 
257
+	return;
258
+      }
259
+    } break;
260
+    };
261
+  }
195 262
 
196
-  if(!size)
197
-    return -1;
263
+}
198 264
 
265
+int AmRtpStream::compile_and_send(const int payload, bool marker, unsigned int ts, 
266
+				  unsigned char* buffer, unsigned int size) {
199 267
   AmRtpPacket rp;
200 268
   rp.payload = payload;
201
-  rp.marker = false;
269
+  rp.timestamp = ts;
270
+  rp.marker = marker;
202 271
   rp.sequence = sequence++;
203
-  rp.timestamp = ts;   
204 272
   rp.ssrc = l_ssrc;
205 273
   rp.compile((unsigned char*)buffer,size);
206 274
 
... ...
@@ -243,6 +311,20 @@ int AmRtpStream::send( unsigned int ts, unsigned char* buffer, unsigned int size
243 311
   return size;
244 312
 }
245 313
 
314
+int AmRtpStream::send( unsigned int ts, unsigned char* buffer, unsigned int size )
315
+{
316
+  if ((mute) || (hold))
317
+    return 0;
318
+
319
+  sendDtmfPacket(ts);
320
+
321
+  if(!size)
322
+    return -1;
323
+
324
+  return compile_and_send(payload, false, ts, buffer, size);
325
+
326
+}
327
+
246 328
 int AmRtpStream::send_raw( char* packet, unsigned int length )
247 329
 {
248 330
   if ((mute) || (hold))
... ...
@@ -351,7 +433,8 @@ AmRtpStream::AmRtpStream(AmSession* _s)
351 433
     mute(false),
352 434
     hold(false),
353 435
     receiving(true),
354
-    monitor_rtp_timeout(true)
436
+    monitor_rtp_timeout(true),
437
+    dtmf_sending_state(DTMF_SEND_NONE)
355 438
 {
356 439
 
357 440
 #ifdef SUPPORT_IPV6
... ...
@@ -625,6 +708,14 @@ int AmRtpStream::getTelephoneEventRate()
625 708
   return retval;
626 709
 }
627 710
 
711
+void AmRtpStream::sendDtmf(int event, unsigned int duration_ms) {
712
+  dtmf_send_queue_mut.lock();
713
+  dtmf_send_queue.push(std::make_pair(event, duration_ms * getTelephoneEventRate() 
714
+				      / 1000));
715
+  dtmf_send_queue_mut.unlock();
716
+  DBG("enqueued DTMF event %i duration %u\n", event, duration_ms);
717
+}
718
+
628 719
 PacketMem::PacketMem() {
629 720
   memset(used, 0, sizeof(used));
630 721
 }
... ...
@@ -43,6 +43,7 @@
43 43
 #include <memory>
44 44
 using std::string;
45 45
 using std::auto_ptr;
46
+using std::pair;
46 47
 
47 48
 // return values of AmRtpStream::receive
48 49
 #define RTP_EMPTY        0 // no rtp packet available
... ...
@@ -141,6 +142,23 @@ protected:
141 142
   
142 143
   AmSession*         session;
143 144
 
145
+  int compile_and_send(const int payload, bool marker, 
146
+		       unsigned int ts, unsigned char* buffer, 
147
+		       unsigned int size);
148
+
149
+
150
+  void sendDtmfPacket(unsigned int ts);
151
+  //       event, duration
152
+  std::queue<pair<int, unsigned int> > dtmf_send_queue;
153
+  AmMutex dtmf_send_queue_mut;
154
+  enum dtmf_sending_state_t {
155
+    DTMF_SEND_NONE,      // not sending event
156
+    DTMF_SEND_SENDING,   // sending event
157
+    DTMF_SEND_ENDING     // sending end of event
158
+  } dtmf_sending_state;
159
+  pair<int, unsigned int> current_send_dtmf;
160
+  unsigned int current_send_dtmf_ts;
161
+  int send_dtmf_end_repeat;
144 162
 
145 163
 public:
146 164
 
... ...
@@ -163,7 +181,7 @@ public:
163 181
   int send( unsigned int ts,
164 182
 	    unsigned char* buffer,
165 183
 	    unsigned int   size );
166
-
184
+  
167 185
   int send_raw( char* packet, unsigned int length );
168 186
 
169 187
   int receive( unsigned char* buffer, unsigned int size,
... ...
@@ -226,6 +244,13 @@ public:
226 244
 
227 245
   int getTelephoneEventRate();
228 246
 
247
+  /**
248
+   * send a DTMF as RTP payload (RFC4733)
249
+   * @param event event ID (e.g. key press), see rfc
250
+   * @param duration_ms duration in milliseconds
251
+   */
252
+  void sendDtmf(int event, unsigned int duration_ms);
253
+
229 254
   /**
230 255
    * Enables RTP stream.
231 256
    * @param sdp_payload payload from the SDP message.
... ...
@@ -584,6 +584,11 @@ void AmSession::putDtmfAudio(const unsigned char *buf, int size, int user_ts)
584 584
   m_dtmfEventQueue.putDtmfAudio(buf, size, user_ts);
585 585
 }
586 586
 
587
+void AmSession::sendDtmf(int event, unsigned int duration_ms) {
588
+  RTPStream()->sendDtmf(event, duration_ms);
589
+}
590
+
591
+
587 592
 void AmSession::onDtmf(int event, int duration_msec)
588 593
 {
589 594
   DBG("AmSession::onDtmf(%i,%i)\n",event,duration_msec);
... ...
@@ -384,6 +384,14 @@ public:
384 384
   bool isDtmfDetectionEnabled() { return m_dtmfDetectionEnabled; }
385 385
   void setDtmfDetectionEnabled(bool e) { m_dtmfDetectionEnabled = e; }
386 386
   void putDtmfAudio(const unsigned char *buf, int size, int user_ts);
387
+
388
+  /**
389
+   * send a DTMF as RTP payload (RFC4733)
390
+   * @param event event ID (e.g. key press), see rfc
391
+   * @param duration_ms duration in milliseconds
392
+   */
393
+  void sendDtmf(int event, unsigned int duration_ms);
394
+
387 395
   /** event handler for apps to use*/
388 396
   virtual void onDtmf(int event, int duration);
389 397
 
... ...
@@ -91,6 +91,10 @@ core actions:
91 91
  enableDTMFDetection() 
92 92
  disableDTMFDetection()
93 93
 
94
+sendDTMF(key [, duration_ms])
95
+  send a DTMF event (RFC4733 event)
96
+  duration_ms defaults to 500ms
97
+
94 98
  B2B.connectCallee(remote_party, remote_uri)
95 99
    connect second leg of B2B session (see AmB2BSession)
96 100