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