blob: e754ca229fd9731c1ed899d87570f49c350ab899 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
jlmiller@webrtc.org5f93d0a2015-01-20 21:36:13 +00003 * Copyright 2012 Google Inc.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00004 *
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
Henrik Kjellander15583c12016-02-10 10:53:12 +010028#include "webrtc/api/dtmfsender.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000029
30#include <set>
31#include <string>
32#include <vector>
33
Henrik Kjellander15583c12016-02-10 10:53:12 +010034#include "webrtc/api/audiotrack.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000035#include "webrtc/base/gunit.h"
36#include "webrtc/base/logging.h"
37#include "webrtc/base/timeutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000038
39using webrtc::AudioTrackInterface;
40using webrtc::AudioTrack;
41using webrtc::DtmfProviderInterface;
42using webrtc::DtmfSender;
43using webrtc::DtmfSenderObserverInterface;
44
45static const char kTestAudioLabel[] = "test_audio_track";
46static const int kMaxWaitMs = 3000;
47
Magnus Jedvertfc950842015-10-12 16:10:43 +020048class FakeDtmfObserver : public DtmfSenderObserverInterface {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000049 public:
50 FakeDtmfObserver() : completed_(false) {}
51
52 // Implements DtmfSenderObserverInterface.
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +000053 void OnToneChange(const std::string& tone) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000054 LOG(LS_VERBOSE) << "FakeDtmfObserver::OnToneChange '" << tone << "'.";
55 tones_.push_back(tone);
56 if (tone.empty()) {
57 completed_ = true;
58 }
59 }
60
61 // getters
62 const std::vector<std::string>& tones() const {
63 return tones_;
64 }
65 bool completed() const {
66 return completed_;
67 }
68
69 private:
70 std::vector<std::string> tones_;
71 bool completed_;
72};
73
74class FakeDtmfProvider : public DtmfProviderInterface {
75 public:
76 struct DtmfInfo {
77 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
86 FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
87
88 ~FakeDtmfProvider() {
89 SignalDestroyed();
90 }
91
92 // Implements DtmfProviderInterface.
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +000093 bool CanInsertDtmf(const std::string& track_label) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000094 return (can_insert_dtmf_tracks_.count(track_label) != 0);
95 }
96
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +000097 bool InsertDtmf(const std::string& track_label,
98 int code,
99 int duration) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000100 int gap = 0;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000101 // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000102 // mockable and use a fake timer in the unit tests.
103 if (last_insert_dtmf_call_ > 0) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000104 gap = static_cast<int>(rtc::Time() - last_insert_dtmf_call_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000105 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000106 last_insert_dtmf_call_ = rtc::Time();
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000107
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
115 virtual sigslot::signal0<>* GetOnDestroyedSignal() {
116 return &SignalDestroyed;
117 }
118
119 // getter and setter
120 const std::vector<DtmfInfo>& dtmf_info_queue() const {
121 return dtmf_info_queue_;
122 }
123
124 // helper functions
125 void AddCanInsertDtmfTrack(const std::string& label) {
126 can_insert_dtmf_tracks_.insert(label);
127 }
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_;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200135 int64_t last_insert_dtmf_call_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000136 sigslot::signal0<> SignalDestroyed;
137};
138
139class DtmfSenderTest : public testing::Test {
140 protected:
141 DtmfSenderTest()
142 : track_(AudioTrack::Create(kTestAudioLabel, NULL)),
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000143 observer_(new rtc::RefCountedObject<FakeDtmfObserver>()),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000144 provider_(new FakeDtmfProvider()) {
145 provider_->AddCanInsertDtmfTrack(kTestAudioLabel);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000146 dtmf_ = DtmfSender::Create(track_, rtc::Thread::Current(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000147 provider_.get());
148 dtmf_->RegisterObserver(observer_.get());
149 }
150
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|.
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
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.
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
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);
wu@webrtc.org8d1e4d62013-09-18 18:01:07 +0000210 // Allow ~100ms error.
211 EXPECT_GE(it_ref->gap, it->gap - 100);
212 EXPECT_LE(it_ref->gap, it->gap + 100);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000213 ++it_ref;
214 ++it;
215 }
216 }
217
218 // Verify the observer got all the expected callbacks.
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
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000233 rtc::scoped_refptr<AudioTrackInterface> track_;
234 rtc::scoped_ptr<FakeDtmfObserver> observer_;
235 rtc::scoped_ptr<FakeDtmfProvider> provider_;
236 rtc::scoped_refptr<DtmfSender> dtmf_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000237};
238
239TEST_F(DtmfSenderTest, CanInsertDtmf) {
240 EXPECT_TRUE(dtmf_->CanInsertDtmf());
241 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
242 EXPECT_FALSE(dtmf_->CanInsertDtmf());
243}
244
245TEST_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
258TEST_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
281TEST_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
295TEST_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
309TEST_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
kjellander@webrtc.orga02d7682015-01-23 14:34:52 +0000328// Flaky when run in parallel.
329// See https://code.google.com/p/webrtc/issues/detail?id=4219.
330TEST_F(DtmfSenderTest, DISABLED_InsertDtmfWithCommaAsDelay) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000331 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
341TEST_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
349TEST_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}