blob: 0a944cb160d7decf513626497d7dff7d88765ccc [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
kjellanderb24317b2016-02-10 07:54:43 -08002 * Copyright 2012 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00003 *
kjellanderb24317b2016-02-10 07:54:43 -08004 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
henrike@webrtc.org28e20752013-07-10 00:45:36 +00009 */
10
Henrik Kjellander15583c12016-02-10 10:53:12 +010011#include "webrtc/api/dtmfsender.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000012
13#include <set>
14#include <string>
15#include <vector>
16
Henrik Kjellander15583c12016-02-10 10:53:12 +010017#include "webrtc/api/audiotrack.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000018#include "webrtc/base/gunit.h"
19#include "webrtc/base/logging.h"
20#include "webrtc/base/timeutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000021
22using webrtc::AudioTrackInterface;
23using webrtc::AudioTrack;
24using webrtc::DtmfProviderInterface;
25using webrtc::DtmfSender;
26using webrtc::DtmfSenderObserverInterface;
27
28static const char kTestAudioLabel[] = "test_audio_track";
29static const int kMaxWaitMs = 3000;
30
Magnus Jedvertfc950842015-10-12 16:10:43 +020031class FakeDtmfObserver : public DtmfSenderObserverInterface {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000032 public:
33 FakeDtmfObserver() : completed_(false) {}
34
35 // Implements DtmfSenderObserverInterface.
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +000036 void OnToneChange(const std::string& tone) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000037 LOG(LS_VERBOSE) << "FakeDtmfObserver::OnToneChange '" << tone << "'.";
38 tones_.push_back(tone);
39 if (tone.empty()) {
40 completed_ = true;
41 }
42 }
43
44 // getters
45 const std::vector<std::string>& tones() const {
46 return tones_;
47 }
48 bool completed() const {
49 return completed_;
50 }
51
52 private:
53 std::vector<std::string> tones_;
54 bool completed_;
55};
56
57class FakeDtmfProvider : public DtmfProviderInterface {
58 public:
59 struct DtmfInfo {
60 DtmfInfo(int code, int duration, int gap)
61 : code(code),
62 duration(duration),
63 gap(gap) {}
64 int code;
65 int duration;
66 int gap;
67 };
68
69 FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
70
71 ~FakeDtmfProvider() {
72 SignalDestroyed();
73 }
74
75 // Implements DtmfProviderInterface.
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +000076 bool CanInsertDtmf(const std::string& track_label) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000077 return (can_insert_dtmf_tracks_.count(track_label) != 0);
78 }
79
kjellander@webrtc.org14665ff2015-03-04 12:58:35 +000080 bool InsertDtmf(const std::string& track_label,
81 int code,
82 int duration) override {
henrike@webrtc.org28e20752013-07-10 00:45:36 +000083 int gap = 0;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000084 // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
henrike@webrtc.org28e20752013-07-10 00:45:36 +000085 // mockable and use a fake timer in the unit tests.
86 if (last_insert_dtmf_call_ > 0) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000087 gap = static_cast<int>(rtc::Time() - last_insert_dtmf_call_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000088 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +000089 last_insert_dtmf_call_ = rtc::Time();
henrike@webrtc.org28e20752013-07-10 00:45:36 +000090
91 LOG(LS_VERBOSE) << "FakeDtmfProvider::InsertDtmf code=" << code
92 << " duration=" << duration
93 << " gap=" << gap << ".";
94 dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
95 return true;
96 }
97
98 virtual sigslot::signal0<>* GetOnDestroyedSignal() {
99 return &SignalDestroyed;
100 }
101
102 // getter and setter
103 const std::vector<DtmfInfo>& dtmf_info_queue() const {
104 return dtmf_info_queue_;
105 }
106
107 // helper functions
108 void AddCanInsertDtmfTrack(const std::string& label) {
109 can_insert_dtmf_tracks_.insert(label);
110 }
111 void RemoveCanInsertDtmfTrack(const std::string& label) {
112 can_insert_dtmf_tracks_.erase(label);
113 }
114
115 private:
116 std::set<std::string> can_insert_dtmf_tracks_;
117 std::vector<DtmfInfo> dtmf_info_queue_;
Peter Boström0c4e06b2015-10-07 12:23:21 +0200118 int64_t last_insert_dtmf_call_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000119 sigslot::signal0<> SignalDestroyed;
120};
121
122class DtmfSenderTest : public testing::Test {
123 protected:
124 DtmfSenderTest()
125 : track_(AudioTrack::Create(kTestAudioLabel, NULL)),
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000126 observer_(new rtc::RefCountedObject<FakeDtmfObserver>()),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000127 provider_(new FakeDtmfProvider()) {
128 provider_->AddCanInsertDtmfTrack(kTestAudioLabel);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000129 dtmf_ = DtmfSender::Create(track_, rtc::Thread::Current(),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000130 provider_.get());
131 dtmf_->RegisterObserver(observer_.get());
132 }
133
134 ~DtmfSenderTest() {
135 if (dtmf_.get()) {
136 dtmf_->UnregisterObserver();
137 }
138 }
139
140 // Constructs a list of DtmfInfo from |tones|, |duration| and
141 // |inter_tone_gap|.
142 void GetDtmfInfoFromString(const std::string& tones, int duration,
143 int inter_tone_gap,
144 std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs) {
145 // Init extra_delay as -inter_tone_gap - duration to ensure the first
146 // DtmfInfo's gap field will be 0.
147 int extra_delay = -1 * (inter_tone_gap + duration);
148
149 std::string::const_iterator it = tones.begin();
150 for (; it != tones.end(); ++it) {
151 char tone = *it;
152 int code = 0;
153 webrtc::GetDtmfCode(tone, &code);
154 if (tone == ',') {
155 extra_delay = 2000; // 2 seconds
156 } else {
157 dtmfs->push_back(FakeDtmfProvider::DtmfInfo(code, duration,
158 duration + inter_tone_gap + extra_delay));
159 extra_delay = 0;
160 }
161 }
162 }
163
164 void VerifyExpectedState(AudioTrackInterface* track,
165 const std::string& tones,
166 int duration, int inter_tone_gap) {
167 EXPECT_EQ(track, dtmf_->track());
168 EXPECT_EQ(tones, dtmf_->tones());
169 EXPECT_EQ(duration, dtmf_->duration());
170 EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
171 }
172
173 // Verify the provider got all the expected calls.
174 void VerifyOnProvider(const std::string& tones, int duration,
175 int inter_tone_gap) {
176 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
177 GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref);
178 VerifyOnProvider(dtmf_queue_ref);
179 }
180
181 void VerifyOnProvider(
182 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
183 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
184 provider_->dtmf_info_queue();
185 ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
186 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
187 dtmf_queue_ref.begin();
188 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
189 dtmf_queue.begin();
190 while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
191 EXPECT_EQ(it_ref->code, it->code);
192 EXPECT_EQ(it_ref->duration, it->duration);
wu@webrtc.org8d1e4d62013-09-18 18:01:07 +0000193 // Allow ~100ms error.
194 EXPECT_GE(it_ref->gap, it->gap - 100);
195 EXPECT_LE(it_ref->gap, it->gap + 100);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000196 ++it_ref;
197 ++it;
198 }
199 }
200
201 // Verify the observer got all the expected callbacks.
202 void VerifyOnObserver(const std::string& tones_ref) {
203 const std::vector<std::string>& tones = observer_->tones();
204 // The observer will get an empty string at the end.
205 EXPECT_EQ(tones_ref.size() + 1, tones.size());
206 EXPECT_TRUE(tones.back().empty());
207 std::string::const_iterator it_ref = tones_ref.begin();
208 std::vector<std::string>::const_iterator it = tones.begin();
209 while (it_ref != tones_ref.end() && it != tones.end()) {
210 EXPECT_EQ(*it_ref, it->at(0));
211 ++it_ref;
212 ++it;
213 }
214 }
215
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52 +0000216 rtc::scoped_refptr<AudioTrackInterface> track_;
217 rtc::scoped_ptr<FakeDtmfObserver> observer_;
218 rtc::scoped_ptr<FakeDtmfProvider> provider_;
219 rtc::scoped_refptr<DtmfSender> dtmf_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220};
221
222TEST_F(DtmfSenderTest, CanInsertDtmf) {
223 EXPECT_TRUE(dtmf_->CanInsertDtmf());
224 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
225 EXPECT_FALSE(dtmf_->CanInsertDtmf());
226}
227
228TEST_F(DtmfSenderTest, InsertDtmf) {
229 std::string tones = "@1%a&*$";
230 int duration = 100;
231 int inter_tone_gap = 50;
232 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
233 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
234
235 // The unrecognized characters should be ignored.
236 std::string known_tones = "1a*";
237 VerifyOnProvider(known_tones, duration, inter_tone_gap);
238 VerifyOnObserver(known_tones);
239}
240
241TEST_F(DtmfSenderTest, InsertDtmfTwice) {
242 std::string tones1 = "12";
243 std::string tones2 = "ab";
244 int duration = 100;
245 int inter_tone_gap = 50;
246 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
247 VerifyExpectedState(track_, tones1, duration, inter_tone_gap);
248 // Wait until the first tone got sent.
249 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
250 VerifyExpectedState(track_, "2", duration, inter_tone_gap);
251 // Insert with another tone buffer.
252 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
253 VerifyExpectedState(track_, tones2, duration, inter_tone_gap);
254 // Wait until it's completed.
255 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
256
257 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
258 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
259 GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
260 VerifyOnProvider(dtmf_queue_ref);
261 VerifyOnObserver("1ab");
262}
263
264TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
265 std::string tones = "@1%a&*$";
266 int duration = 100;
267 int inter_tone_gap = 50;
268 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
269 // Wait until the first tone got sent.
270 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
271 // Delete provider.
272 provider_.reset();
273 // The queue should be discontinued so no more tone callbacks.
274 WAIT(false, 200);
275 EXPECT_EQ(1U, observer_->tones().size());
276}
277
278TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
279 std::string tones = "@1%a&*$";
280 int duration = 100;
281 int inter_tone_gap = 50;
282 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
283 // Wait until the first tone got sent.
284 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
285 // Delete the sender.
286 dtmf_ = NULL;
287 // The queue should be discontinued so no more tone callbacks.
288 WAIT(false, 200);
289 EXPECT_EQ(1U, observer_->tones().size());
290}
291
292TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
293 std::string tones1 = "12";
294 std::string tones2 = "";
295 int duration = 100;
296 int inter_tone_gap = 50;
297 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
298 // Wait until the first tone got sent.
299 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
300 // Insert with another tone buffer.
301 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
302 // Wait until it's completed.
303 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
304
305 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
306 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
307 VerifyOnProvider(dtmf_queue_ref);
308 VerifyOnObserver("1");
309}
310
kjellander@webrtc.orga02d7682015-01-23 14:34:52 +0000311// Flaky when run in parallel.
312// See https://code.google.com/p/webrtc/issues/detail?id=4219.
313TEST_F(DtmfSenderTest, DISABLED_InsertDtmfWithCommaAsDelay) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000314 std::string tones = "3,4";
315 int duration = 100;
316 int inter_tone_gap = 50;
317 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
318 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
319
320 VerifyOnProvider(tones, duration, inter_tone_gap);
321 VerifyOnObserver(tones);
322}
323
324TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
325 std::string tones = "3,4";
326 int duration = 100;
327 int inter_tone_gap = 50;
328 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
329 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
330}
331
332TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
333 std::string tones = "3,4";
334 int duration = 100;
335 int inter_tone_gap = 50;
336
337 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
338 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 69, inter_tone_gap));
339 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 49));
340
341 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
342}