1 /*
2  * Copyright (C) 2005 Andriy I Pylypenko
3  *
4  * This file is part of SEMS, a free SIP media server.
5  *
6  * SEMS is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version. This program is released under
10  * the GPL with the additional exemption that compiling, linking,
11  * and/or using OpenSSL is allowed.
12  *
13  * For a license to use the SEMS software under conditions
14  * other than those described here, or to purchase support for this
15  * software, please contact iptel.org by e-mail at the following addresses:
16  *    info@iptel.org
17  *
18  * SEMS is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27 /** @file AmDtmfDetector.h */
28 #ifndef _AmDtmfDetector_h_
29 #define _AmDtmfDetector_h_
30 
31 #include "AmEventQueue.h"
32 #include "rtp/telephone_event.h"
33 
34 #include <string>
35 #include <memory>
36 
37 #ifdef USE_SPANDSP
38 #include <math.h>
39 #ifndef HAVE_OLD_SPANDSP_CALLBACK
40 #include "spandsp.h"
41 #else
42 #include "spandsp/tone_detect.h"
43 #endif
44 #endif
45 
46 
47 //
48 // Forward declarations
49 //
50 class AmSession;
51 class AmDtmfDetector;
52 class AmDtmfEventHandler;
53 class AmRequest;
54 
55 namespace Dtmf
56 {
57   enum EventSource { SOURCE_RTP, SOURCE_SIP, SOURCE_INBAND, SOURCE_DETECTOR };
58 
59   enum InbandDetectorType { SEMSInternal, SpanDSP };
60 };
61 /**
62  * \brief sink for audio to be processed by the inband DTMF detector
63  *
64  * This class adds processing of timeouts for DTMF detection
65  */
66 class AmDtmfEventQueue : public AmEventQueue
67 {
68  private:
69   AmDtmfDetector *m_detector;
70 
71  public:
72   AmDtmfEventQueue(AmDtmfDetector *);
73   /**
74    * Reimplemented abstract method from AmEventQueue
75    */
76   void processEvents();
77   void putDtmfAudio(const unsigned char *, int size, unsigned long long system_ts);
78 };
79 
80 /**
81  * \brief Base class for DTMF events
82  */
83 class AmDtmfEvent : public AmEvent
84 {
85  protected:
86   /**
87    * Code of the key
88    */
89   int m_event;
90   /**
91    * Duration of keypress in miliseconds
92    */
93   int m_duration_msec;
94 
95   /**
96    * Constructor
97    */
AmDtmfEvent(int id)98   AmDtmfEvent(int id)
99     : AmEvent(id)
100     {
101     }
102 
103  public:
AmDtmfEvent(int event,int duration)104   AmDtmfEvent(int event, int duration)
105     : AmEvent(Dtmf::SOURCE_DETECTOR), m_event(event), m_duration_msec(duration)
106     {
107     }
108   /**
109    * Code of the key
110    */
event()111   int event() const { return m_event; }
112   /**
113    * Duration of keypress in miliseconds
114    */
duration()115   int duration() const { return m_duration_msec; }
116 };
117 /**
118  * \brief Interface for a sink for KeyPresses.
119  */
120 class AmKeyPressSink {
121  public:
AmKeyPressSink()122   AmKeyPressSink() { }
~AmKeyPressSink()123   virtual ~AmKeyPressSink() { }
124 
125   /**
126    * Through this method the AmDtmfDetector receives events that was
127    * detected by specific detectors.
128    * @param event code of key pressed
129    * @param source which detector posted this event
130    * @param start time when key was pressed
131    * @param stop time when key was released
132    * @param has_eventid whether event has an id
133    * @param event_id id of the event
134    */
135   virtual void registerKeyReleased(int event, Dtmf::EventSource source,
136 				   const struct timeval& start, const struct timeval& stop,
137 				   bool has_eventid = false, unsigned int event_id = 0) = 0;
138   /**
139    * Through this method the AmDtmfDetector receives events that was
140    * detected by specific detectors.
141    * @param event code of key released
142    * @param source which detector posted this event
143    * @param has_eventid whether event has an id
144    * @param event_id id of the event
145    */
146   virtual void registerKeyPressed(int event, Dtmf::EventSource source, bool has_eventid=false, unsigned int event_id=0) = 0;
147   /**
148    *   Flush (report to session) any pending key if ti matches the event id
149    *   @param  event_id ID of the event (e.g. RTP TS)
150    */
151   virtual void flushKey(unsigned int event_id) = 0;
152 };
153 
154 /**
155  * \brief DTMF received via RTP
156  */
157 class AmRtpDtmfEvent : public AmDtmfEvent
158 {
159  private:
160   /**
161    * E flag from RTP packet
162    */
163   int m_e;
164   /**
165    * Volume value from RTP packet
166    */
167   int m_volume;
168 
169   /**
170    * RTP timestamp
171    */
172   unsigned int m_ts;
173 
174  public:
175   /**
176    * Constructor
177    * @param payload data from rtp packet of payload type telephone-event
178    * @param sample_rate sampling rate (from SDP payload description)
179    */
180   AmRtpDtmfEvent(const dtmf_payload_t *payload, int sample_rate, unsigned int ts);
181 
182   /**
183    * Volume value from RTP packet
184    */
volume()185   int volume() { return m_volume; }
186   /**
187    * E flag from RTP packet
188    */
e()189   int e() { return m_e; }
190 
ts()191   unsigned int ts() { return m_ts; }
192 };
193 
194 /**
195  * \brief DTMF received via SIP INFO request
196  */
197 class AmSipDtmfEvent : public AmDtmfEvent
198 {
199  private:
200   /**
201    * Parser for application/dtmf-relay
202    */
203   void parseRequestBody(const string&);
204   /**
205    * Parser for application/dtmf-relay
206    */
207   void parseLine(const string&);
208 
209  public:
210   /**
211    * Constructor
212    */
213   AmSipDtmfEvent(const string& request_body);
214 };
215 
216 /** the inband DTMF detector interface */
217 class AmInbandDtmfDetector
218 {
219  protected:
220   /** here key presses are reported to */
221   AmKeyPressSink *m_keysink;
222 
223  public:
224   AmInbandDtmfDetector(AmKeyPressSink *keysink);
~AmInbandDtmfDetector()225   virtual ~AmInbandDtmfDetector() { }
226   /**
227    * Entry point for audio stream
228    */
229   virtual int streamPut(const unsigned char* samples, unsigned int size, unsigned long long system_ts) = 0;
230 };
231 
232 /**
233  * \brief Inband DTMF detector
234  *
235  * This class implements detection of DTMF from audio stream
236  */
237 class AmSemsInbandDtmfDetector
238 : public AmInbandDtmfDetector
239 {
240  private:
241   /**
242    * Time when first audio packet containing current DTMF tone was detected
243    */
244   struct timeval m_startTime;
245 
246   static const int REL_DTMF_NPOINTS = 205;    /* Number of samples for DTMF recognition */
247   static const int REL_NCOEFF = 8;            /* number of frequencies to be analyzed   */
248 
249   const int SAMPLERATE;
250   /**
251    * DTMF recognition successfull only if no less than DTMF_INTERVAL
252    * audio packets were processed and all gave the same result
253    */
254   static const int DTMF_INTERVAL = 3;
255 
256   /* For DTMF recognition:
257    * 2 * cos(2 * PI * k / N) precalculated for all k
258    */
259   int rel_cos2pik[REL_NCOEFF];
260 
261   int m_buf[REL_DTMF_NPOINTS];
262   char m_last;
263   int m_idx;
264   int m_result[16];
265   int m_lastCode;
266   int m_last_ts;	// timestamp representative for the currently analysed filter block
267 
268   int m_count;
269 
270   void isdn_audio_goertzel_relative();
271   void isdn_audio_eval_dtmf_relative();
272   void isdn_audio_calc_dtmf(const signed short* buf, int len, unsigned int ts);
273 
274  public:
275   AmSemsInbandDtmfDetector(AmKeyPressSink *keysink, int sample_rate);
276   ~AmSemsInbandDtmfDetector();
277   /**
278    * Entry point for audio stream
279    */
280   int streamPut(const unsigned char* samples, unsigned int size, unsigned long long system_ts);
281 };
282 
283 
284 #ifdef USE_SPANDSP
285 
286 class AmSpanDSPInbandDtmfDetector
287 : public AmInbandDtmfDetector  {
288 
289   struct timeval key_start;
290   int m_lastCode;
291   dtmf_rx_state_t* rx_state;
292 
293   static void tone_report_func(void *user_data, int code
294 #ifndef HAVE_OLD_SPANDSP_CALLBACK
295 			       , int level, int delay
296 #endif
297 			       );
298 
299   void tone_report_f(int code, int level, int delay);
300   int char2int(char code);
301 /*   static void dtmf_rx_callback(void* user_data, const char* digits, int len);  */
302 /*   void dtmf_rx_f(const char* digits, int len); */
303 
304  public:
305   AmSpanDSPInbandDtmfDetector(AmKeyPressSink *keysink, int sample_rate);
306   ~AmSpanDSPInbandDtmfDetector();
307 
308   /**
309    * Entry point for audio stream
310    */
311   int streamPut(const unsigned char* samples, unsigned int size, unsigned long long system_ts);
312 };
313 #endif // USE_SPANDSP
314 
315 
316 /**
317  * \brief SIP INFO DTMF detector
318  *
319  * This class implements detection of DTMF from audio stream
320  */
321 class AmSipDtmfDetector
322 {
323  private:
324   AmKeyPressSink *m_keysink;
325 
326  public:
327   AmSipDtmfDetector(AmKeyPressSink *keysink);
328   void process(AmSipDtmfEvent *event);
329 };
330 
331 /**
332  * \brief RTP DTMF detector
333  *
334  * This class implements detection of DTMF sent via RTP
335  */
336 class AmRtpDtmfDetector
337 {
338  private:
339   /**
340    *  sink for detected keys
341    */
342   AmKeyPressSink *m_keysink;
343   /**
344    * Is there event pending?
345    */
346   bool m_eventPending;
347   int m_currentEvent;
348   unsigned int m_currentTS;
349   bool m_currentTS_i;
350   int m_packetCount;
351 
352   unsigned int m_lastTS;
353   bool m_lastTS_i;
354 
355   // after MAX_PACKET_WAIT packets with no RTP DTMF packets received,
356   // a RTP DTMF event is sent out to the aggregating detector
357   static const int MAX_PACKET_WAIT = 8;
358   /**
359    * Time when first packet for current event was received
360    */
361   struct timeval m_startTime;
362 
363   /**
364    * Send out pending event
365    */
366   void sendPending();
367 
368  public:
369   /**
370    * Constructor
371    * @param keysink is the sink for detected keys
372    */
373   AmRtpDtmfDetector(AmKeyPressSink *keysink);
374   /**
375    * Process RTP DTMF event
376    */
377   void process(AmRtpDtmfEvent *event);
378   void checkTimeout();
379 };
380 
381 /**
382  * \brief DTMF sink class
383  *
384  * This class is an interface for components that whish to
385  * receive DTMF event notification.
386  */
387 class AmDtmfSink
388 {
389 public:
390   virtual void postDtmfEvent(AmDtmfEvent *) = 0;
~AmDtmfSink()391   virtual ~AmDtmfSink() { }
392 };
393 
394 /**
395  * \brief DTMF detector class
396  *
397  * This class collects DTMF info from three sources: RTP (RFC 2833),
398  * SIP INFO method (RFC 2976) and DTMF tones from audio stream.
399  * Received DTMF events are further reported to SEMS application via
400  * DialogState::onDtmf() call.
401  */
402 class AmDtmfDetector
403 : public AmEventHandler,
404   public AmKeyPressSink
405 {
406  private:
407   static const int WAIT_TIMEOUT = 200; // miliseconds
408   /**
409    * Session this class belongs to.
410    */
411   AmDtmfSink *m_dtmfSink;
412   AmRtpDtmfDetector m_rtpDetector;
413   AmSipDtmfDetector m_sipDetector;
414   std::unique_ptr<AmInbandDtmfDetector> m_inbandDetector;
415   Dtmf::InbandDetectorType m_inband_type;
416 
417   struct timeval m_startTime;
418   struct timeval m_lastReportTime;
419   int m_currentEvent;
420   bool m_eventPending;
421 
422   bool m_current_eventid_i;
423   unsigned int m_current_eventid;
424 
425   bool m_sipEventReceived;
426   bool m_inbandEventReceived;
427   bool m_rtpEventReceived;
428 
429   AmMutex m_reportLock;
430 
431   /**
432    * Implementation of AmEventHandler::process().
433    * Processes events from AmMediaProcessor.
434    * @see AmEventHandler
435    */
436   virtual void process(AmEvent *);
437 
438   void reportEvent();
439 
440   /**
441    * Through this method the AmDtmfDetector receives events that was
442    * detected by specific detectors.
443    * @param event code of key pressed
444    * @param source which detector posted this event
445    * @param start time when key was pressed
446    * @param stop time when key was released
447    */
448   void registerKeyReleased(int event, Dtmf::EventSource source,
449 			   const struct timeval& start, const struct timeval& stop,
450 			   bool has_eventid = false, unsigned int event_id = 0);
451   /**
452    * Through this method the AmDtmfDetector receives events that was
453    * detected by specific detectors.
454    * @param event code of key released
455    * @param source which detector posted this event
456    */
457   void registerKeyPressed(int event, Dtmf::EventSource source, bool has_eventid=false, unsigned int event_id=0);
458 
459   void flushKey(unsigned int event_id);
460 
461  public:
462   /**
463    * Constructor
464    * @param session is the owner of this class instance
465    */
466   AmDtmfDetector(AmDtmfSink *dtmf_sink);
~AmDtmfDetector()467   virtual ~AmDtmfDetector() {}
468 
469   void checkTimeout();
470   void putDtmfAudio(const unsigned char *, int size, unsigned long long system_ts);
471 
472   void setInbandDetector(Dtmf::InbandDetectorType t, int sample_rate);
473   friend class AmSipDtmfDetector;
474   friend class AmRtpDtmfDetector;
475   friend class AmInbandDtmfDetector;
476 };
477 #endif // _AmDtmfDetector_h_
478