blob: efe568da842c0cb18d9580911541fdd31018ba10 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:361/*
kjellanderb24317b2016-02-10 15:54:432 * Copyright 2012 The WebRTC project authors. All Rights Reserved.
henrike@webrtc.org28e20752013-07-10 00:45:363 *
kjellanderb24317b2016-02-10 15:54:434 * 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:369 */
10
Henrik Kjellander15583c12016-02-10 09:53:1211#include "webrtc/api/dtmfsender.h"
henrike@webrtc.org28e20752013-07-10 00:45:3612
kwibergd1fe2812016-04-27 13:47:2913#include <memory>
henrike@webrtc.org28e20752013-07-10 00:45:3614#include <set>
15#include <string>
16#include <vector>
17
Henrik Kjellander15583c12016-02-10 09:53:1218#include "webrtc/api/audiotrack.h"
buildbot@webrtc.orgd4e598d2014-07-29 17:36:5219#include "webrtc/base/gunit.h"
20#include "webrtc/base/logging.h"
21#include "webrtc/base/timeutils.h"
henrike@webrtc.org28e20752013-07-10 00:45:3622
23using webrtc::AudioTrackInterface;
24using webrtc::AudioTrack;
25using webrtc::DtmfProviderInterface;
26using webrtc::DtmfSender;
27using webrtc::DtmfSenderObserverInterface;
28
29static const char kTestAudioLabel[] = "test_audio_track";
30static const int kMaxWaitMs = 3000;
31
Magnus Jedvertfc950842015-10-12 14:10:4332class FakeDtmfObserver : public DtmfSenderObserverInterface {
henrike@webrtc.org28e20752013-07-10 00:45:3633 public:
34 FakeDtmfObserver() : completed_(false) {}
35
36 // Implements DtmfSenderObserverInterface.
kjellander@webrtc.org14665ff2015-03-04 12:58:3537 void OnToneChange(const std::string& tone) override {
henrike@webrtc.org28e20752013-07-10 00:45:3638 LOG(LS_VERBOSE) << "FakeDtmfObserver::OnToneChange '" << tone << "'.";
39 tones_.push_back(tone);
40 if (tone.empty()) {
41 completed_ = true;
42 }
43 }
44
45 // getters
46 const std::vector<std::string>& tones() const {
47 return tones_;
48 }
49 bool completed() const {
50 return completed_;
51 }
52
53 private:
54 std::vector<std::string> tones_;
55 bool completed_;
56};
57
58class FakeDtmfProvider : public DtmfProviderInterface {
59 public:
60 struct DtmfInfo {
61 DtmfInfo(int code, int duration, int gap)
62 : code(code),
63 duration(duration),
64 gap(gap) {}
65 int code;
66 int duration;
67 int gap;
68 };
69
70 FakeDtmfProvider() : last_insert_dtmf_call_(0) {}
71
72 ~FakeDtmfProvider() {
73 SignalDestroyed();
74 }
75
76 // Implements DtmfProviderInterface.
kjellander@webrtc.org14665ff2015-03-04 12:58:3577 bool CanInsertDtmf(const std::string& track_label) override {
henrike@webrtc.org28e20752013-07-10 00:45:3678 return (can_insert_dtmf_tracks_.count(track_label) != 0);
79 }
80
kjellander@webrtc.org14665ff2015-03-04 12:58:3581 bool InsertDtmf(const std::string& track_label,
82 int code,
83 int duration) override {
henrike@webrtc.org28e20752013-07-10 00:45:3684 int gap = 0;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:5285 // TODO(ronghuawu): Make the timer (basically the rtc::TimeNanos)
henrike@webrtc.org28e20752013-07-10 00:45:3686 // mockable and use a fake timer in the unit tests.
87 if (last_insert_dtmf_call_ > 0) {
buildbot@webrtc.orgd4e598d2014-07-29 17:36:5288 gap = static_cast<int>(rtc::Time() - last_insert_dtmf_call_);
henrike@webrtc.org28e20752013-07-10 00:45:3689 }
buildbot@webrtc.orgd4e598d2014-07-29 17:36:5290 last_insert_dtmf_call_ = rtc::Time();
henrike@webrtc.org28e20752013-07-10 00:45:3691
92 LOG(LS_VERBOSE) << "FakeDtmfProvider::InsertDtmf code=" << code
93 << " duration=" << duration
94 << " gap=" << gap << ".";
95 dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
96 return true;
97 }
98
nisseef8b61e2016-04-29 13:09:1599 sigslot::signal0<>* GetOnDestroyedSignal() override {
henrike@webrtc.org28e20752013-07-10 00:45:36100 return &SignalDestroyed;
101 }
102
103 // getter and setter
104 const std::vector<DtmfInfo>& dtmf_info_queue() const {
105 return dtmf_info_queue_;
106 }
107
108 // helper functions
109 void AddCanInsertDtmfTrack(const std::string& label) {
110 can_insert_dtmf_tracks_.insert(label);
111 }
112 void RemoveCanInsertDtmfTrack(const std::string& label) {
113 can_insert_dtmf_tracks_.erase(label);
114 }
115
116 private:
117 std::set<std::string> can_insert_dtmf_tracks_;
118 std::vector<DtmfInfo> dtmf_info_queue_;
Peter Boström0c4e06b2015-10-07 10:23:21119 int64_t last_insert_dtmf_call_;
henrike@webrtc.org28e20752013-07-10 00:45:36120 sigslot::signal0<> SignalDestroyed;
121};
122
123class DtmfSenderTest : public testing::Test {
124 protected:
125 DtmfSenderTest()
126 : track_(AudioTrack::Create(kTestAudioLabel, NULL)),
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52127 observer_(new rtc::RefCountedObject<FakeDtmfObserver>()),
henrike@webrtc.org28e20752013-07-10 00:45:36128 provider_(new FakeDtmfProvider()) {
129 provider_->AddCanInsertDtmfTrack(kTestAudioLabel);
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52130 dtmf_ = DtmfSender::Create(track_, rtc::Thread::Current(),
henrike@webrtc.org28e20752013-07-10 00:45:36131 provider_.get());
132 dtmf_->RegisterObserver(observer_.get());
133 }
134
135 ~DtmfSenderTest() {
136 if (dtmf_.get()) {
137 dtmf_->UnregisterObserver();
138 }
139 }
140
141 // Constructs a list of DtmfInfo from |tones|, |duration| and
142 // |inter_tone_gap|.
143 void GetDtmfInfoFromString(const std::string& tones, int duration,
144 int inter_tone_gap,
145 std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs) {
146 // Init extra_delay as -inter_tone_gap - duration to ensure the first
147 // DtmfInfo's gap field will be 0.
148 int extra_delay = -1 * (inter_tone_gap + duration);
149
150 std::string::const_iterator it = tones.begin();
151 for (; it != tones.end(); ++it) {
152 char tone = *it;
153 int code = 0;
154 webrtc::GetDtmfCode(tone, &code);
155 if (tone == ',') {
156 extra_delay = 2000; // 2 seconds
157 } else {
158 dtmfs->push_back(FakeDtmfProvider::DtmfInfo(code, duration,
159 duration + inter_tone_gap + extra_delay));
160 extra_delay = 0;
161 }
162 }
163 }
164
165 void VerifyExpectedState(AudioTrackInterface* track,
166 const std::string& tones,
167 int duration, int inter_tone_gap) {
168 EXPECT_EQ(track, dtmf_->track());
169 EXPECT_EQ(tones, dtmf_->tones());
170 EXPECT_EQ(duration, dtmf_->duration());
171 EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
172 }
173
174 // Verify the provider got all the expected calls.
175 void VerifyOnProvider(const std::string& tones, int duration,
176 int inter_tone_gap) {
177 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
178 GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref);
179 VerifyOnProvider(dtmf_queue_ref);
180 }
181
182 void VerifyOnProvider(
183 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
184 const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
185 provider_->dtmf_info_queue();
186 ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
187 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
188 dtmf_queue_ref.begin();
189 std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
190 dtmf_queue.begin();
191 while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
192 EXPECT_EQ(it_ref->code, it->code);
193 EXPECT_EQ(it_ref->duration, it->duration);
wu@webrtc.org8d1e4d62013-09-18 18:01:07194 // Allow ~100ms error.
195 EXPECT_GE(it_ref->gap, it->gap - 100);
196 EXPECT_LE(it_ref->gap, it->gap + 100);
henrike@webrtc.org28e20752013-07-10 00:45:36197 ++it_ref;
198 ++it;
199 }
200 }
201
202 // Verify the observer got all the expected callbacks.
203 void VerifyOnObserver(const std::string& tones_ref) {
204 const std::vector<std::string>& tones = observer_->tones();
205 // The observer will get an empty string at the end.
206 EXPECT_EQ(tones_ref.size() + 1, tones.size());
207 EXPECT_TRUE(tones.back().empty());
208 std::string::const_iterator it_ref = tones_ref.begin();
209 std::vector<std::string>::const_iterator it = tones.begin();
210 while (it_ref != tones_ref.end() && it != tones.end()) {
211 EXPECT_EQ(*it_ref, it->at(0));
212 ++it_ref;
213 ++it;
214 }
215 }
216
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52217 rtc::scoped_refptr<AudioTrackInterface> track_;
kwibergd1fe2812016-04-27 13:47:29218 std::unique_ptr<FakeDtmfObserver> observer_;
219 std::unique_ptr<FakeDtmfProvider> provider_;
buildbot@webrtc.orgd4e598d2014-07-29 17:36:52220 rtc::scoped_refptr<DtmfSender> dtmf_;
henrike@webrtc.org28e20752013-07-10 00:45:36221};
222
223TEST_F(DtmfSenderTest, CanInsertDtmf) {
224 EXPECT_TRUE(dtmf_->CanInsertDtmf());
225 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
226 EXPECT_FALSE(dtmf_->CanInsertDtmf());
227}
228
229TEST_F(DtmfSenderTest, InsertDtmf) {
230 std::string tones = "@1%a&*$";
231 int duration = 100;
232 int inter_tone_gap = 50;
233 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
234 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
235
236 // The unrecognized characters should be ignored.
237 std::string known_tones = "1a*";
238 VerifyOnProvider(known_tones, duration, inter_tone_gap);
239 VerifyOnObserver(known_tones);
240}
241
242TEST_F(DtmfSenderTest, InsertDtmfTwice) {
243 std::string tones1 = "12";
244 std::string tones2 = "ab";
245 int duration = 100;
246 int inter_tone_gap = 50;
247 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
248 VerifyExpectedState(track_, tones1, duration, inter_tone_gap);
249 // Wait until the first tone got sent.
250 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
251 VerifyExpectedState(track_, "2", duration, inter_tone_gap);
252 // Insert with another tone buffer.
253 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
254 VerifyExpectedState(track_, tones2, duration, inter_tone_gap);
255 // Wait until it's completed.
256 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
257
258 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
259 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
260 GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
261 VerifyOnProvider(dtmf_queue_ref);
262 VerifyOnObserver("1ab");
263}
264
265TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
266 std::string tones = "@1%a&*$";
267 int duration = 100;
268 int inter_tone_gap = 50;
269 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
270 // Wait until the first tone got sent.
271 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
272 // Delete provider.
273 provider_.reset();
274 // The queue should be discontinued so no more tone callbacks.
275 WAIT(false, 200);
276 EXPECT_EQ(1U, observer_->tones().size());
277}
278
279TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
280 std::string tones = "@1%a&*$";
281 int duration = 100;
282 int inter_tone_gap = 50;
283 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
284 // Wait until the first tone got sent.
285 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
286 // Delete the sender.
287 dtmf_ = NULL;
288 // The queue should be discontinued so no more tone callbacks.
289 WAIT(false, 200);
290 EXPECT_EQ(1U, observer_->tones().size());
291}
292
293TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
294 std::string tones1 = "12";
295 std::string tones2 = "";
296 int duration = 100;
297 int inter_tone_gap = 50;
298 EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
299 // Wait until the first tone got sent.
300 EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
301 // Insert with another tone buffer.
302 EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
303 // Wait until it's completed.
304 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
305
306 std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
307 GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
308 VerifyOnProvider(dtmf_queue_ref);
309 VerifyOnObserver("1");
310}
311
kjellander@webrtc.orga02d7682015-01-23 14:34:52312// Flaky when run in parallel.
313// See https://code.google.com/p/webrtc/issues/detail?id=4219.
314TEST_F(DtmfSenderTest, DISABLED_InsertDtmfWithCommaAsDelay) {
henrike@webrtc.org28e20752013-07-10 00:45:36315 std::string tones = "3,4";
316 int duration = 100;
317 int inter_tone_gap = 50;
318 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
319 EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
320
321 VerifyOnProvider(tones, duration, inter_tone_gap);
322 VerifyOnObserver(tones);
323}
324
325TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
326 std::string tones = "3,4";
327 int duration = 100;
328 int inter_tone_gap = 50;
329 provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
330 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
331}
332
333TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
334 std::string tones = "3,4";
335 int duration = 100;
336 int inter_tone_gap = 50;
337
338 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
339 EXPECT_FALSE(dtmf_->InsertDtmf(tones, 69, inter_tone_gap));
340 EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 49));
341
342 EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
343}