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 <set>
31 #include <string>
32 #include <vector>
33
34 #include "talk/app/webrtc/audiotrack.h"
35 #include "webrtc/base/gunit.h"
36 #include "webrtc/base/logging.h"
37 #include "webrtc/base/timeutils.h"
38
39 using webrtc::AudioTrackInterface;
40 using webrtc::AudioTrack;
41 using webrtc::DtmfProviderInterface;
42 using webrtc::DtmfSender;
43 using webrtc::DtmfSenderObserverInterface;
44
45 static const char kTestAudioLabel[] = "test_audio_track";
46 static const int kMaxWaitMs = 3000;
47
48 class FakeDtmfObserver : public DtmfSenderObserverInterface {
49 public:
FakeDtmfObserver()50 FakeDtmfObserver() : completed_(false) {}
51
52 // Implements DtmfSenderObserverInterface.
OnToneChange(const std::string & tone)53 void OnToneChange(const std::string& tone) override {
54 LOG(LS_VERBOSE) << "FakeDtmfObserver::OnToneChange '" << tone << "'.";
55 tones_.push_back(tone);
56 if (tone.empty()) {
57 completed_ = true;
58 }
59 }
60
61 // getters
tones() const62 const std::vector<std::string>& tones() const {
63 return tones_;
64 }
completed() const65 bool completed() const {
66 return completed_;
67 }
68
69 private:
70 std::vector<std::string> tones_;
71 bool completed_;
72 };
73
74 class FakeDtmfProvider : public DtmfProviderInterface {
75 public:
76 struct DtmfInfo {
DtmfInfoFakeDtmfProvider::DtmfInfo77 DtmfInfo(int code, int duration, int gap)
78 : code(code),
79 duration(duration),
80 gap(gap) {}
81 int code;
82 int duration;
83 int gap;
84 };
85
FakeDtmfProvider()86 FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
87
~FakeDtmfProvider()88 ~FakeDtmfProvider() {
89 SignalDestroyed();
90 }
91
92 // Implements DtmfProviderInterface.
CanInsertDtmf(const std::string & track_label)93 bool CanInsertDtmf(const std::string& track_label) override {
94 return (can_insert_dtmf_tracks_.count(track_label) != 0);
95 }
96
InsertDtmf(const std::string & track_label,int code,int duration)97 bool InsertDtmf(const std::string& track_label,
98 int code,
99 int duration) override {
100 int gap = 0;
101 // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
102 // mockable and use a fake timer in the unit tests.
103 if (last_insert_dtmf_call_ > 0) {
104 gap = static_cast<int>(rtc::Time() - last_insert_dtmf_call_);
105 }
106 last_insert_dtmf_call_ = rtc::Time();
107
108 LOG(LS_VERBOSE) << "FakeDtmfProvider::InsertDtmf code=" << code
109 << " duration=" << duration
110 << " gap=" << gap << ".";
111 dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
112 return true;
113 }
114
GetOnDestroyedSignal()115 virtual sigslot::signal0<>* GetOnDestroyedSignal() {
116 return &SignalDestroyed;
117 }
118
119 // getter and setter
dtmf_info_queue() const120 const std::vector<DtmfInfo>& dtmf_info_queue() const {
121 return dtmf_info_queue_;
122 }
123
124 // helper functions
AddCanInsertDtmfTrack(const std::string & label)125 void AddCanInsertDtmfTrack(const std::string& label) {
126 can_insert_dtmf_tracks_.insert(label);
127 }
RemoveCanInsertDtmfTrack(const std::string & label)128 void RemoveCanInsertDtmfTrack(const std::string& label) {
129 can_insert_dtmf_tracks_.erase(label);
130 }
131
132 private:
133 std::set<std::string> can_insert_dtmf_tracks_;
134 std::vector<DtmfInfo> dtmf_info_queue_;
135 int64_t last_insert_dtmf_call_;
136 sigslot::signal0<> SignalDestroyed;
137 };
138
139 class DtmfSenderTest : public testing::Test {
140 protected:
DtmfSenderTest()141 DtmfSenderTest()
142 : track_(AudioTrack::Create(kTestAudioLabel, NULL)),
143 observer_(new rtc::RefCountedObject<FakeDtmfObserver>()),
144 provider_(new FakeDtmfProvider()) {
145 provider_->AddCanInsertDtmfTrack(kTestAudioLabel);
146 dtmf_ = DtmfSender::Create(track_, rtc::Thread::Current(),
147 provider_.get());
148 dtmf_->RegisterObserver(observer_.get());
149 }
150
~DtmfSenderTest()151 ~DtmfSenderTest() {
152 if (dtmf_.get()) {
153 dtmf_->UnregisterObserver();
154 }
155 }
156
157 // Constructs a list of DtmfInfo from |tones|, |duration| and
158 // |inter_tone_gap|.
GetDtmfInfoFromString(const std::string & tones,int duration,int inter_tone_gap,std::vector<FakeDtmfProvider::DtmfInfo> * dtmfs)159 void GetDtmfInfoFromString(const std::string& tones, int duration,
160 int inter_tone_gap,
161 std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs) {
162 // Init extra_delay as -inter_tone_gap - duration to ensure the first
163 // DtmfInfo's gap field will be 0.
164 int extra_delay = -1 * (inter_tone_gap + duration);
165
166 std::string::const_iterator it = tones.begin();
167 for (; it != tones.end(); ++it) {
168 char tone = *it;
169 int code = 0;
170 webrtc::GetDtmfCode(tone, &code);
171 if (tone == ',') {
172 extra_delay = 2000; // 2 seconds
173 } else {
174 dtmfs->push_back(FakeDtmfProvider::DtmfInfo(code, duration,
175 duration + inter_tone_gap + extra_delay));
176 extra_delay = 0;
177 }
178 }
179 }
180
VerifyExpectedState(AudioTrackInterface * track,const std::string & tones,int duration,int inter_tone_gap)181 void VerifyExpectedState(AudioTrackInterface* track,
182 const std::string& tones,
183 int duration, int inter_tone_gap) {
184 EXPECT_EQ(track, dtmf_->track());
185 EXPECT_EQ(tones, dtmf_->tones());
186 EXPECT_EQ(duration, dtmf_->duration());
187 EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
188 }
189
190 // Verify the provider got all the expected calls.
VerifyOnProvider(const std::string & tones,int duration,int inter_tone_gap)191 void VerifyOnProvider(const std::string& tones, int duration,
192 int inter_tone_gap) {
193 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
194 GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref);
195 VerifyOnProvider(dtmf_queue_ref);
196 }
197
VerifyOnProvider(const std::vector<FakeDtmfProvider::DtmfInfo> & dtmf_queue_ref)198 void VerifyOnProvider(
199 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
200 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
201 provider_->dtmf_info_queue();
202 ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
203 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
204 dtmf_queue_ref.begin();
205 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
206 dtmf_queue.begin();
207 while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
208 EXPECT_EQ(it_ref->code, it->code);
209 EXPECT_EQ(it_ref->duration, it->duration);
210 // Allow ~100ms error.
211 EXPECT_GE(it_ref->gap, it->gap - 100);
212 EXPECT_LE(it_ref->gap, it->gap + 100);
213 ++it_ref;
214 ++it;
215 }
216 }
217
218 // Verify the observer got all the expected callbacks.
VerifyOnObserver(const std::string & tones_ref)219 void VerifyOnObserver(const std::string& tones_ref) {
220 const std::vector<std::string>& tones = observer_->tones();
221 // The observer will get an empty string at the end.
222 EXPECT_EQ(tones_ref.size() + 1, tones.size());
223 EXPECT_TRUE(tones.back().empty());
224 std::string::const_iterator it_ref = tones_ref.begin();
225 std::vector<std::string>::const_iterator it = tones.begin();
226 while (it_ref != tones_ref.end() && it != tones.end()) {
227 EXPECT_EQ(*it_ref, it->at(0));
228 ++it_ref;
229 ++it;
230 }
231 }
232
233 rtc::scoped_refptr<AudioTrackInterface> track_;
234 rtc::scoped_ptr<FakeDtmfObserver> observer_;
235 rtc::scoped_ptr<FakeDtmfProvider> provider_;
236 rtc::scoped_refptr<DtmfSender> dtmf_;
237 };
238
TEST_F(DtmfSenderTest,CanInsertDtmf)239 TEST_F(DtmfSenderTest, CanInsertDtmf) {
240 EXPECT_TRUE(dtmf_->CanInsertDtmf());
241 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
242 EXPECT_FALSE(dtmf_->CanInsertDtmf());
243 }
244
TEST_F(DtmfSenderTest,InsertDtmf)245 TEST_F(DtmfSenderTest, InsertDtmf) {
246 std::string tones = "@1%a&*$";
247 int duration = 100;
248 int inter_tone_gap = 50;
249 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
250 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
251
252 // The unrecognized characters should be ignored.
253 std::string known_tones = "1a*";
254 VerifyOnProvider(known_tones, duration, inter_tone_gap);
255 VerifyOnObserver(known_tones);
256 }
257
TEST_F(DtmfSenderTest,InsertDtmfTwice)258 TEST_F(DtmfSenderTest, InsertDtmfTwice) {
259 std::string tones1 = "12";
260 std::string tones2 = "ab";
261 int duration = 100;
262 int inter_tone_gap = 50;
263 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
264 VerifyExpectedState(track_, tones1, duration, inter_tone_gap);
265 // Wait until the first tone got sent.
266 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
267 VerifyExpectedState(track_, "2", duration, inter_tone_gap);
268 // Insert with another tone buffer.
269 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
270 VerifyExpectedState(track_, tones2, duration, inter_tone_gap);
271 // Wait until it's completed.
272 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
273
274 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
275 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
276 GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
277 VerifyOnProvider(dtmf_queue_ref);
278 VerifyOnObserver("1ab");
279 }
280
TEST_F(DtmfSenderTest,InsertDtmfWhileProviderIsDeleted)281 TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
282 std::string tones = "@1%a&*$";
283 int duration = 100;
284 int inter_tone_gap = 50;
285 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
286 // Wait until the first tone got sent.
287 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
288 // Delete provider.
289 provider_.reset();
290 // The queue should be discontinued so no more tone callbacks.
291 WAIT(false, 200);
292 EXPECT_EQ(1U, observer_->tones().size());
293 }
294
TEST_F(DtmfSenderTest,InsertDtmfWhileSenderIsDeleted)295 TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
296 std::string tones = "@1%a&*$";
297 int duration = 100;
298 int inter_tone_gap = 50;
299 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
300 // Wait until the first tone got sent.
301 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
302 // Delete the sender.
303 dtmf_ = NULL;
304 // The queue should be discontinued so no more tone callbacks.
305 WAIT(false, 200);
306 EXPECT_EQ(1U, observer_->tones().size());
307 }
308
TEST_F(DtmfSenderTest,InsertEmptyTonesToCancelPreviousTask)309 TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
310 std::string tones1 = "12";
311 std::string tones2 = "";
312 int duration = 100;
313 int inter_tone_gap = 50;
314 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
315 // Wait until the first tone got sent.
316 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
317 // Insert with another tone buffer.
318 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
319 // Wait until it's completed.
320 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
321
322 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
323 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
324 VerifyOnProvider(dtmf_queue_ref);
325 VerifyOnObserver("1");
326 }
327
328 // Flaky when run in parallel.
329 // See https://code.google.com/p/webrtc/issues/detail?id=4219.
TEST_F(DtmfSenderTest,DISABLED_InsertDtmfWithCommaAsDelay)330 TEST_F(DtmfSenderTest, DISABLED_InsertDtmfWithCommaAsDelay) {
331 std::string tones = "3,4";
332 int duration = 100;
333 int inter_tone_gap = 50;
334 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
335 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
336
337 VerifyOnProvider(tones, duration, inter_tone_gap);
338 VerifyOnObserver(tones);
339 }
340
TEST_F(DtmfSenderTest,TryInsertDtmfWhenItDoesNotWork)341 TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
342 std::string tones = "3,4";
343 int duration = 100;
344 int inter_tone_gap = 50;
345 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
346 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
347 }
348
TEST_F(DtmfSenderTest,InsertDtmfWithInvalidDurationOrGap)349 TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
350 std::string tones = "3,4";
351 int duration = 100;
352 int inter_tone_gap = 50;
353
354 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
355 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 69, inter_tone_gap));
356 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 49));
357
358 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
359 }
360