1 /* 2 * libjingle 3 * Copyright 2012, Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "talk/app/webrtc/dtmfsender.h" 29 30 #include <ctype.h> 31 32 #include <string> 33 34 #include "talk/base/logging.h" 35 #include "talk/base/thread.h" 36 37 namespace webrtc { 38 39 enum { 40 MSG_DO_INSERT_DTMF = 0, 41 }; 42 43 // RFC4733 44 // +-------+--------+------+---------+ 45 // | Event | Code | Type | Volume? | 46 // +-------+--------+------+---------+ 47 // | 0--9 | 0--9 | tone | yes | 48 // | * | 10 | tone | yes | 49 // | # | 11 | tone | yes | 50 // | A--D | 12--15 | tone | yes | 51 // +-------+--------+------+---------+ 52 // The "," is a special event defined by the WebRTC spec. It means to delay for 53 // 2 seconds before processing the next tone. We use -1 as its code. 54 static const int kDtmfCodeTwoSecondDelay = -1; 55 static const int kDtmfTwoSecondInMs = 2000; 56 static const char kDtmfValidTones[] = ",0123456789*#ABCDabcd"; 57 static const char kDtmfTonesTable[] = ",0123456789*#ABCD"; 58 // The duration cannot be more than 6000ms or less than 70ms. The gap between 59 // tones must be at least 50 ms. 60 static const int kDtmfDefaultDurationMs = 100; 61 static const int kDtmfMinDurationMs = 70; 62 static const int kDtmfMaxDurationMs = 6000; 63 static const int kDtmfDefaultGapMs = 50; 64 static const int kDtmfMinGapMs = 50; 65 66 // Get DTMF code from the DTMF event character. 67 bool GetDtmfCode(char tone, int* code) { 68 // Convert a-d to A-D. 69 char event = toupper(tone); 70 const char* p = strchr(kDtmfTonesTable, event); 71 if (!p) { 72 return false; 73 } 74 *code = p - kDtmfTonesTable - 1; 75 return true; 76 } 77 78 talk_base::scoped_refptr<DtmfSender> DtmfSender::Create( 79 AudioTrackInterface* track, 80 talk_base::Thread* signaling_thread, 81 DtmfProviderInterface* provider) { 82 if (!track || !signaling_thread) { 83 return NULL; 84 } 85 talk_base::scoped_refptr<DtmfSender> dtmf_sender( 86 new talk_base::RefCountedObject<DtmfSender>(track, signaling_thread, 87 provider)); 88 return dtmf_sender; 89 } 90 91 DtmfSender::DtmfSender(AudioTrackInterface* track, 92 talk_base::Thread* signaling_thread, 93 DtmfProviderInterface* provider) 94 : track_(track), 95 observer_(NULL), 96 signaling_thread_(signaling_thread), 97 provider_(provider), 98 duration_(kDtmfDefaultDurationMs), 99 inter_tone_gap_(kDtmfDefaultGapMs) { 100 ASSERT(track_ != NULL); 101 ASSERT(signaling_thread_ != NULL); 102 if (provider_) { 103 ASSERT(provider_->GetOnDestroyedSignal() != NULL); 104 provider_->GetOnDestroyedSignal()->connect( 105 this, &DtmfSender::OnProviderDestroyed); 106 } 107 } 108 109 DtmfSender::~DtmfSender() { 110 if (provider_) { 111 ASSERT(provider_->GetOnDestroyedSignal() != NULL); 112 provider_->GetOnDestroyedSignal()->disconnect(this); 113 } 114 StopSending(); 115 } 116 117 void DtmfSender::RegisterObserver(DtmfSenderObserverInterface* observer) { 118 observer_ = observer; 119 } 120 121 void DtmfSender::UnregisterObserver() { 122 observer_ = NULL; 123 } 124 125 bool DtmfSender::CanInsertDtmf() { 126 ASSERT(signaling_thread_->IsCurrent()); 127 if (!provider_) { 128 return false; 129 } 130 return provider_->CanInsertDtmf(track_->id()); 131 } 132 133 bool DtmfSender::InsertDtmf(const std::string& tones, int duration, 134 int inter_tone_gap) { 135 ASSERT(signaling_thread_->IsCurrent()); 136 137 if (duration > kDtmfMaxDurationMs || 138 duration < kDtmfMinDurationMs || 139 inter_tone_gap < kDtmfMinGapMs) { 140 LOG(LS_ERROR) << "InsertDtmf is called with invalid duration or tones gap. " 141 << "The duration cannot be more than " << kDtmfMaxDurationMs 142 << "ms or less than " << kDtmfMinDurationMs << "ms. " 143 << "The gap between tones must be at least " << kDtmfMinGapMs << "ms."; 144 return false; 145 } 146 147 if (!CanInsertDtmf()) { 148 LOG(LS_ERROR) 149 << "InsertDtmf is called on DtmfSender that can't send DTMF."; 150 return false; 151 } 152 153 tones_ = tones; 154 duration_ = duration; 155 inter_tone_gap_ = inter_tone_gap; 156 // Clear the previous queue. 157 signaling_thread_->Clear(this, MSG_DO_INSERT_DTMF); 158 // Kick off a new DTMF task queue. 159 signaling_thread_->Post(this, MSG_DO_INSERT_DTMF); 160 return true; 161 } 162 163 const AudioTrackInterface* DtmfSender::track() const { 164 return track_; 165 } 166 167 std::string DtmfSender::tones() const { 168 return tones_; 169 } 170 171 int DtmfSender::duration() const { 172 return duration_; 173 } 174 175 int DtmfSender::inter_tone_gap() const { 176 return inter_tone_gap_; 177 } 178 179 void DtmfSender::OnMessage(talk_base::Message* msg) { 180 switch (msg->message_id) { 181 case MSG_DO_INSERT_DTMF: { 182 DoInsertDtmf(); 183 break; 184 } 185 default: { 186 ASSERT(false); 187 break; 188 } 189 } 190 } 191 192 void DtmfSender::DoInsertDtmf() { 193 ASSERT(signaling_thread_->IsCurrent()); 194 195 // Get the first DTMF tone from the tone buffer. Unrecognized characters will 196 // be ignored and skipped. 197 size_t first_tone_pos = tones_.find_first_of(kDtmfValidTones); 198 int code = 0; 199 if (first_tone_pos == std::string::npos) { 200 tones_.clear(); 201 // Fire a OnToneChange event with an empty string and stop. 202 if (observer_) { 203 observer_->OnToneChange(std::string()); 204 } 205 return; 206 } else { 207 char tone = tones_[first_tone_pos]; 208 if (!GetDtmfCode(tone, &code)) { 209 // The find_first_of(kDtmfValidTones) should have guarantee |tone| is 210 // a valid DTMF tone. 211 ASSERT(false); 212 } 213 } 214 215 int tone_gap = inter_tone_gap_; 216 if (code == kDtmfCodeTwoSecondDelay) { 217 // Special case defined by WebRTC - The character',' indicates a delay of 2 218 // seconds before processing the next character in the tones parameter. 219 tone_gap = kDtmfTwoSecondInMs; 220 } else { 221 if (!provider_) { 222 LOG(LS_ERROR) << "The DtmfProvider has been destroyed."; 223 return; 224 } 225 // The provider starts playout of the given tone on the 226 // associated RTP media stream, using the appropriate codec. 227 if (!provider_->InsertDtmf(track_->id(), code, duration_)) { 228 LOG(LS_ERROR) << "The DtmfProvider can no longer send DTMF."; 229 return; 230 } 231 // Wait for the number of milliseconds specified by |duration_|. 232 tone_gap += duration_; 233 } 234 235 // Fire a OnToneChange event with the tone that's just processed. 236 if (observer_) { 237 observer_->OnToneChange(tones_.substr(first_tone_pos, 1)); 238 } 239 240 // Erase the unrecognized characters plus the tone that's just processed. 241 tones_.erase(0, first_tone_pos + 1); 242 243 // Continue with the next tone. 244 signaling_thread_->PostDelayed(tone_gap, this, MSG_DO_INSERT_DTMF); 245 } 246 247 void DtmfSender::OnProviderDestroyed() { 248 LOG(LS_INFO) << "The Dtmf provider is deleted. Clear the sending queue."; 249 StopSending(); 250 provider_ = NULL; 251 } 252 253 void DtmfSender::StopSending() { 254 signaling_thread_->Clear(this); 255 } 256 257 } // namespace webrtc 258