• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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