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 "talk/base/gunit.h"
     36 #include "talk/base/logging.h"
     37 #include "talk/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   virtual 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   virtual bool CanInsertDtmf(const std::string&  track_label) OVERRIDE {
     94     return (can_insert_dtmf_tracks_.count(track_label) != 0);
     95   }
     96 
     97   virtual bool InsertDtmf(const std::string& track_label,
     98                           int code, int duration) OVERRIDE {
     99     int gap = 0;
    100     // TODO(ronghuawu): Make the timer (basically the talk_base::TimeNanos)
    101     // mockable and use a fake timer in the unit tests.
    102     if (last_insert_dtmf_call_ > 0) {
    103       gap = static_cast<int>(talk_base::Time() - last_insert_dtmf_call_);
    104     }
    105     last_insert_dtmf_call_ = talk_base::Time();
    106 
    107     LOG(LS_VERBOSE) << "FakeDtmfProvider::InsertDtmf code=" << code
    108                     << " duration=" << duration
    109                     << " gap=" << gap << ".";
    110     dtmf_info_queue_.push_back(DtmfInfo(code, duration, gap));
    111     return true;
    112   }
    113 
    114   virtual sigslot::signal0<>* GetOnDestroyedSignal() {
    115     return &SignalDestroyed;
    116   }
    117 
    118   // getter and setter
    119   const std::vector<DtmfInfo>& dtmf_info_queue() const {
    120     return dtmf_info_queue_;
    121   }
    122 
    123   // helper functions
    124   void AddCanInsertDtmfTrack(const std::string& label) {
    125     can_insert_dtmf_tracks_.insert(label);
    126   }
    127   void RemoveCanInsertDtmfTrack(const std::string& label) {
    128     can_insert_dtmf_tracks_.erase(label);
    129   }
    130 
    131  private:
    132   std::set<std::string> can_insert_dtmf_tracks_;
    133   std::vector<DtmfInfo> dtmf_info_queue_;
    134   int64 last_insert_dtmf_call_;
    135   sigslot::signal0<> SignalDestroyed;
    136 };
    137 
    138 class DtmfSenderTest : public testing::Test {
    139  protected:
    140   DtmfSenderTest()
    141       : track_(AudioTrack::Create(kTestAudioLabel, NULL)),
    142         observer_(new talk_base::RefCountedObject<FakeDtmfObserver>()),
    143         provider_(new FakeDtmfProvider()) {
    144     provider_->AddCanInsertDtmfTrack(kTestAudioLabel);
    145     dtmf_ = DtmfSender::Create(track_, talk_base::Thread::Current(),
    146                                provider_.get());
    147     dtmf_->RegisterObserver(observer_.get());
    148   }
    149 
    150   ~DtmfSenderTest() {
    151     if (dtmf_.get()) {
    152       dtmf_->UnregisterObserver();
    153     }
    154   }
    155 
    156   // Constructs a list of DtmfInfo from |tones|, |duration| and
    157   // |inter_tone_gap|.
    158   void GetDtmfInfoFromString(const std::string& tones, int duration,
    159                              int inter_tone_gap,
    160                              std::vector<FakeDtmfProvider::DtmfInfo>* dtmfs) {
    161     // Init extra_delay as -inter_tone_gap - duration to ensure the first
    162     // DtmfInfo's gap field will be 0.
    163     int extra_delay = -1 * (inter_tone_gap + duration);
    164 
    165     std::string::const_iterator it = tones.begin();
    166     for (; it != tones.end(); ++it) {
    167       char tone = *it;
    168       int code = 0;
    169       webrtc::GetDtmfCode(tone, &code);
    170       if (tone == ',') {
    171         extra_delay = 2000;  // 2 seconds
    172       } else {
    173         dtmfs->push_back(FakeDtmfProvider::DtmfInfo(code, duration,
    174                          duration + inter_tone_gap + extra_delay));
    175         extra_delay = 0;
    176       }
    177     }
    178   }
    179 
    180   void VerifyExpectedState(AudioTrackInterface* track,
    181                           const std::string& tones,
    182                           int duration, int inter_tone_gap) {
    183     EXPECT_EQ(track, dtmf_->track());
    184     EXPECT_EQ(tones, dtmf_->tones());
    185     EXPECT_EQ(duration, dtmf_->duration());
    186     EXPECT_EQ(inter_tone_gap, dtmf_->inter_tone_gap());
    187   }
    188 
    189   // Verify the provider got all the expected calls.
    190   void VerifyOnProvider(const std::string& tones, int duration,
    191                         int inter_tone_gap) {
    192     std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
    193     GetDtmfInfoFromString(tones, duration, inter_tone_gap, &dtmf_queue_ref);
    194     VerifyOnProvider(dtmf_queue_ref);
    195   }
    196 
    197   void VerifyOnProvider(
    198       const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue_ref) {
    199     const std::vector<FakeDtmfProvider::DtmfInfo>& dtmf_queue =
    200         provider_->dtmf_info_queue();
    201     ASSERT_EQ(dtmf_queue_ref.size(), dtmf_queue.size());
    202     std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it_ref =
    203         dtmf_queue_ref.begin();
    204     std::vector<FakeDtmfProvider::DtmfInfo>::const_iterator it =
    205         dtmf_queue.begin();
    206     while (it_ref != dtmf_queue_ref.end() && it != dtmf_queue.end()) {
    207       EXPECT_EQ(it_ref->code, it->code);
    208       EXPECT_EQ(it_ref->duration, it->duration);
    209       // Allow ~100ms error.
    210       EXPECT_GE(it_ref->gap, it->gap - 100);
    211       EXPECT_LE(it_ref->gap, it->gap + 100);
    212       ++it_ref;
    213       ++it;
    214     }
    215   }
    216 
    217   // Verify the observer got all the expected callbacks.
    218   void VerifyOnObserver(const std::string& tones_ref) {
    219     const std::vector<std::string>& tones = observer_->tones();
    220     // The observer will get an empty string at the end.
    221     EXPECT_EQ(tones_ref.size() + 1, tones.size());
    222     EXPECT_TRUE(tones.back().empty());
    223     std::string::const_iterator it_ref = tones_ref.begin();
    224     std::vector<std::string>::const_iterator it = tones.begin();
    225     while (it_ref != tones_ref.end() && it != tones.end()) {
    226       EXPECT_EQ(*it_ref, it->at(0));
    227       ++it_ref;
    228       ++it;
    229     }
    230   }
    231 
    232   talk_base::scoped_refptr<AudioTrackInterface> track_;
    233   talk_base::scoped_ptr<FakeDtmfObserver> observer_;
    234   talk_base::scoped_ptr<FakeDtmfProvider> provider_;
    235   talk_base::scoped_refptr<DtmfSender> dtmf_;
    236 };
    237 
    238 TEST_F(DtmfSenderTest, CanInsertDtmf) {
    239   EXPECT_TRUE(dtmf_->CanInsertDtmf());
    240   provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
    241   EXPECT_FALSE(dtmf_->CanInsertDtmf());
    242 }
    243 
    244 TEST_F(DtmfSenderTest, InsertDtmf) {
    245   std::string tones = "@1%a&*$";
    246   int duration = 100;
    247   int inter_tone_gap = 50;
    248   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
    249   EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
    250 
    251   // The unrecognized characters should be ignored.
    252   std::string known_tones = "1a*";
    253   VerifyOnProvider(known_tones, duration, inter_tone_gap);
    254   VerifyOnObserver(known_tones);
    255 }
    256 
    257 TEST_F(DtmfSenderTest, InsertDtmfTwice) {
    258   std::string tones1 = "12";
    259   std::string tones2 = "ab";
    260   int duration = 100;
    261   int inter_tone_gap = 50;
    262   EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
    263   VerifyExpectedState(track_, tones1, duration, inter_tone_gap);
    264   // Wait until the first tone got sent.
    265   EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
    266   VerifyExpectedState(track_, "2", duration, inter_tone_gap);
    267   // Insert with another tone buffer.
    268   EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
    269   VerifyExpectedState(track_, tones2, duration, inter_tone_gap);
    270   // Wait until it's completed.
    271   EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
    272 
    273   std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
    274   GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
    275   GetDtmfInfoFromString("ab", duration, inter_tone_gap, &dtmf_queue_ref);
    276   VerifyOnProvider(dtmf_queue_ref);
    277   VerifyOnObserver("1ab");
    278 }
    279 
    280 TEST_F(DtmfSenderTest, InsertDtmfWhileProviderIsDeleted) {
    281   std::string tones = "@1%a&*$";
    282   int duration = 100;
    283   int inter_tone_gap = 50;
    284   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
    285   // Wait until the first tone got sent.
    286   EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
    287   // Delete provider.
    288   provider_.reset();
    289   // The queue should be discontinued so no more tone callbacks.
    290   WAIT(false, 200);
    291   EXPECT_EQ(1U, observer_->tones().size());
    292 }
    293 
    294 TEST_F(DtmfSenderTest, InsertDtmfWhileSenderIsDeleted) {
    295   std::string tones = "@1%a&*$";
    296   int duration = 100;
    297   int inter_tone_gap = 50;
    298   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
    299   // Wait until the first tone got sent.
    300   EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
    301   // Delete the sender.
    302   dtmf_ = NULL;
    303   // The queue should be discontinued so no more tone callbacks.
    304   WAIT(false, 200);
    305   EXPECT_EQ(1U, observer_->tones().size());
    306 }
    307 
    308 TEST_F(DtmfSenderTest, InsertEmptyTonesToCancelPreviousTask) {
    309   std::string tones1 = "12";
    310   std::string tones2 = "";
    311   int duration = 100;
    312   int inter_tone_gap = 50;
    313   EXPECT_TRUE(dtmf_->InsertDtmf(tones1, duration, inter_tone_gap));
    314   // Wait until the first tone got sent.
    315   EXPECT_TRUE_WAIT(observer_->tones().size() == 1, kMaxWaitMs);
    316   // Insert with another tone buffer.
    317   EXPECT_TRUE(dtmf_->InsertDtmf(tones2, duration, inter_tone_gap));
    318   // Wait until it's completed.
    319   EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
    320 
    321   std::vector<FakeDtmfProvider::DtmfInfo> dtmf_queue_ref;
    322   GetDtmfInfoFromString("1", duration, inter_tone_gap, &dtmf_queue_ref);
    323   VerifyOnProvider(dtmf_queue_ref);
    324   VerifyOnObserver("1");
    325 }
    326 
    327 TEST_F(DtmfSenderTest, InsertDtmfWithCommaAsDelay) {
    328   std::string tones = "3,4";
    329   int duration = 100;
    330   int inter_tone_gap = 50;
    331   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
    332   EXPECT_TRUE_WAIT(observer_->completed(), kMaxWaitMs);
    333 
    334   VerifyOnProvider(tones, duration, inter_tone_gap);
    335   VerifyOnObserver(tones);
    336 }
    337 
    338 TEST_F(DtmfSenderTest, TryInsertDtmfWhenItDoesNotWork) {
    339   std::string tones = "3,4";
    340   int duration = 100;
    341   int inter_tone_gap = 50;
    342   provider_->RemoveCanInsertDtmfTrack(kTestAudioLabel);
    343   EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
    344 }
    345 
    346 TEST_F(DtmfSenderTest, InsertDtmfWithInvalidDurationOrGap) {
    347   std::string tones = "3,4";
    348   int duration = 100;
    349   int inter_tone_gap = 50;
    350 
    351   EXPECT_FALSE(dtmf_->InsertDtmf(tones, 6001, inter_tone_gap));
    352   EXPECT_FALSE(dtmf_->InsertDtmf(tones, 69, inter_tone_gap));
    353   EXPECT_FALSE(dtmf_->InsertDtmf(tones, duration, 49));
    354 
    355   EXPECT_TRUE(dtmf_->InsertDtmf(tones, duration, inter_tone_gap));
    356 }
    357