Home | History | Annotate | Download | only in webrtc
      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:
     50   FakeDtmfObserver() : completed_(false) {}
     51 
     52   // Implements DtmfSenderObserverInterface.
     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
     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 
     74 class 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.
     93   bool CanInsertDtmf(const std::string& track_label) override {
     94     return (can_insert_dtmf_tracks_.count(track_label) != 0);
     95   }
     96 
     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 
    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_;
    135   int64_t last_insert_dtmf_call_;
    136   sigslot::signal0<> SignalDestroyed;
    137 };
    138 
    139 class DtmfSenderTest : public testing::Test {
    140  protected:
    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 
    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);
    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.
    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 
    239 TEST_F(DtmfSenderTest, CanInsertDtmf) {
    240   EXPECT_TRUE(dtmf_->CanInsertDtmf());
    241   provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
    242   EXPECT_FALSE(dtmf_->CanInsertDtmf());
    243 }
    244 
    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 
    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 
    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 
    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 
    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.
    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 
    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 
    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