Home | History | Annotate | Download | only in capture
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "content/browser/media/capture/web_contents_audio_input_stream.h"
      6 
      7 #include <list>
      8 
      9 #include "base/bind.h"
     10 #include "base/bind_helpers.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/synchronization/waitable_event.h"
     13 #include "base/threading/thread.h"
     14 #include "content/browser/media/capture/audio_mirroring_manager.h"
     15 #include "content/browser/media/capture/web_contents_tracker.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "content/public/test/test_browser_thread_bundle.h"
     18 #include "media/audio/simple_sources.h"
     19 #include "media/audio/virtual_audio_input_stream.h"
     20 #include "testing/gmock/include/gmock/gmock.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 
     23 using ::testing::_;
     24 using ::testing::Assign;
     25 using ::testing::DoAll;
     26 using ::testing::Invoke;
     27 using ::testing::InvokeWithoutArgs;
     28 using ::testing::NotNull;
     29 using ::testing::SaveArg;
     30 using ::testing::WithArgs;
     31 
     32 using media::AudioInputStream;
     33 using media::AudioOutputStream;
     34 using media::AudioParameters;
     35 using media::SineWaveAudioSource;
     36 using media::VirtualAudioInputStream;
     37 using media::VirtualAudioOutputStream;
     38 
     39 namespace content {
     40 
     41 namespace {
     42 
     43 const int kRenderProcessId = 123;
     44 const int kRenderFrameId = 456;
     45 const int kAnotherRenderProcessId = 789;
     46 const int kAnotherRenderFrameId = 1;
     47 
     48 const AudioParameters& TestAudioParameters() {
     49   static const AudioParameters params(
     50       AudioParameters::AUDIO_FAKE,
     51       media::CHANNEL_LAYOUT_STEREO,
     52       AudioParameters::kAudioCDSampleRate, 16,
     53       AudioParameters::kAudioCDSampleRate / 100);
     54   return params;
     55 }
     56 
     57 class MockAudioMirroringManager : public AudioMirroringManager {
     58  public:
     59   MockAudioMirroringManager() : AudioMirroringManager() {}
     60   virtual ~MockAudioMirroringManager() {}
     61 
     62   MOCK_METHOD1(StartMirroring, void(MirroringDestination* destination));
     63   MOCK_METHOD1(StopMirroring, void(MirroringDestination* destination));
     64 
     65  private:
     66   DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager);
     67 };
     68 
     69 class MockWebContentsTracker : public WebContentsTracker {
     70  public:
     71   MockWebContentsTracker() : WebContentsTracker(false) {}
     72 
     73   MOCK_METHOD3(Start,
     74                void(int render_process_id, int render_frame_id,
     75                     const ChangeCallback& callback));
     76   MOCK_METHOD0(Stop, void());
     77 
     78  private:
     79   virtual ~MockWebContentsTracker() {}
     80 
     81   DISALLOW_COPY_AND_ASSIGN(MockWebContentsTracker);
     82 };
     83 
     84 // A fully-functional VirtualAudioInputStream, but methods are mocked to allow
     85 // tests to check how/when they are invoked.
     86 class MockVirtualAudioInputStream : public VirtualAudioInputStream {
     87  public:
     88   explicit MockVirtualAudioInputStream(
     89       const scoped_refptr<base::MessageLoopProxy>& worker_loop)
     90       : VirtualAudioInputStream(TestAudioParameters(), worker_loop,
     91                                 VirtualAudioInputStream::AfterCloseCallback()),
     92         real_(TestAudioParameters(), worker_loop,
     93               base::Bind(&MockVirtualAudioInputStream::OnRealStreamHasClosed,
     94                          base::Unretained(this))),
     95         real_stream_is_closed_(false) {
     96     // Set default actions of mocked methods to delegate to the concrete
     97     // implementation.
     98     ON_CALL(*this, Open())
     99         .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Open));
    100     ON_CALL(*this, Start(_))
    101         .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Start));
    102     ON_CALL(*this, Stop())
    103         .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Stop));
    104     ON_CALL(*this, Close())
    105         .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Close));
    106     ON_CALL(*this, GetMaxVolume())
    107         .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::GetMaxVolume));
    108     ON_CALL(*this, SetVolume(_))
    109         .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::SetVolume));
    110     ON_CALL(*this, GetVolume())
    111         .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::GetVolume));
    112     ON_CALL(*this, SetAutomaticGainControl(_))
    113         .WillByDefault(
    114             Invoke(&real_, &VirtualAudioInputStream::SetAutomaticGainControl));
    115     ON_CALL(*this, GetAutomaticGainControl())
    116         .WillByDefault(
    117             Invoke(&real_, &VirtualAudioInputStream::GetAutomaticGainControl));
    118     ON_CALL(*this, AddOutputStream(NotNull(), _))
    119         .WillByDefault(
    120             Invoke(&real_, &VirtualAudioInputStream::AddOutputStream));
    121     ON_CALL(*this, RemoveOutputStream(NotNull(), _))
    122         .WillByDefault(
    123             Invoke(&real_, &VirtualAudioInputStream::RemoveOutputStream));
    124   }
    125 
    126   ~MockVirtualAudioInputStream() {
    127     DCHECK(real_stream_is_closed_);
    128   }
    129 
    130   MOCK_METHOD0(Open, bool());
    131   MOCK_METHOD1(Start, void(AudioInputStream::AudioInputCallback*));
    132   MOCK_METHOD0(Stop, void());
    133   MOCK_METHOD0(Close, void());
    134   MOCK_METHOD0(GetMaxVolume, double());
    135   MOCK_METHOD1(SetVolume, void(double));
    136   MOCK_METHOD0(GetVolume, double());
    137   MOCK_METHOD1(SetAutomaticGainControl, void(bool));
    138   MOCK_METHOD0(GetAutomaticGainControl, bool());
    139   MOCK_METHOD2(AddOutputStream, void(VirtualAudioOutputStream*,
    140                                      const AudioParameters&));
    141   MOCK_METHOD2(RemoveOutputStream, void(VirtualAudioOutputStream*,
    142                                         const AudioParameters&));
    143 
    144  private:
    145   void OnRealStreamHasClosed(VirtualAudioInputStream* stream) {
    146     DCHECK_EQ(&real_, stream);
    147     DCHECK(!real_stream_is_closed_);
    148     real_stream_is_closed_ = true;
    149   }
    150 
    151   VirtualAudioInputStream real_;
    152   bool real_stream_is_closed_;
    153 
    154   DISALLOW_COPY_AND_ASSIGN(MockVirtualAudioInputStream);
    155 };
    156 
    157 class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
    158  public:
    159   MockAudioInputCallback() {}
    160 
    161   MOCK_METHOD4(OnData,
    162                void(AudioInputStream* stream,
    163                     const media::AudioBus* src,
    164                     uint32 hardware_delay_bytes,
    165                     double volume));
    166   MOCK_METHOD1(OnError, void(AudioInputStream* stream));
    167 
    168  private:
    169   DISALLOW_COPY_AND_ASSIGN(MockAudioInputCallback);
    170 };
    171 
    172 }  // namespace
    173 
    174 class WebContentsAudioInputStreamTest : public testing::Test {
    175  public:
    176   WebContentsAudioInputStreamTest()
    177       : thread_bundle_(new TestBrowserThreadBundle(
    178             TestBrowserThreadBundle::REAL_IO_THREAD)),
    179         audio_thread_("Audio thread"),
    180         mock_mirroring_manager_(new MockAudioMirroringManager()),
    181         mock_tracker_(new MockWebContentsTracker()),
    182         mock_vais_(NULL),
    183         wcais_(NULL),
    184         destination_(NULL),
    185         current_render_process_id_(kRenderProcessId),
    186         current_render_frame_id_(kRenderFrameId),
    187         on_data_event_(false, false) {
    188     audio_thread_.Start();
    189   }
    190 
    191   virtual ~WebContentsAudioInputStreamTest() {
    192     audio_thread_.Stop();
    193     thread_bundle_.reset();
    194 
    195     DCHECK(!mock_vais_);
    196     DCHECK(!wcais_);
    197     EXPECT_FALSE(destination_);
    198     DCHECK(streams_.empty());
    199     DCHECK(sources_.empty());
    200   }
    201 
    202   void Open() {
    203     mock_vais_ =
    204         new MockVirtualAudioInputStream(audio_thread_.message_loop_proxy());
    205     EXPECT_CALL(*mock_vais_, Open());
    206     EXPECT_CALL(*mock_vais_, Close());  // At Close() time.
    207 
    208     ASSERT_EQ(kRenderProcessId, current_render_process_id_);
    209     ASSERT_EQ(kRenderFrameId, current_render_frame_id_);
    210     EXPECT_CALL(*mock_tracker_.get(),
    211                 Start(kRenderProcessId, kRenderFrameId, _))
    212         .WillOnce(DoAll(
    213              SaveArg<2>(&change_callback_),
    214              WithArgs<0, 1>(Invoke(this,
    215                                    &WebContentsAudioInputStreamTest::
    216                                        SimulateChangeCallback))));
    217 
    218     EXPECT_CALL(*mock_tracker_.get(), Stop());  // At Close() time.
    219 
    220     wcais_ = new WebContentsAudioInputStream(
    221         current_render_process_id_, current_render_frame_id_,
    222         mock_mirroring_manager_.get(),
    223         mock_tracker_, mock_vais_);
    224     wcais_->Open();
    225   }
    226 
    227   void Start() {
    228     EXPECT_CALL(*mock_vais_, Start(&mock_input_callback_));
    229     EXPECT_CALL(*mock_vais_, Stop());  // At Stop() time.
    230 
    231     EXPECT_CALL(*mock_mirroring_manager_, StartMirroring(NotNull()))
    232         .WillOnce(SaveArg<0>(&destination_))
    233         .RetiresOnSaturation();
    234     // At Stop() time, or when the mirroring target changes:
    235     EXPECT_CALL(*mock_mirroring_manager_, StopMirroring(NotNull()))
    236         .WillOnce(Assign(
    237             &destination_,
    238             static_cast<AudioMirroringManager::MirroringDestination*>(NULL)))
    239         .RetiresOnSaturation();
    240 
    241     EXPECT_CALL(mock_input_callback_, OnData(NotNull(), NotNull(), _, _))
    242         .WillRepeatedly(
    243             InvokeWithoutArgs(&on_data_event_, &base::WaitableEvent::Signal));
    244 
    245     wcais_->Start(&mock_input_callback_);
    246 
    247     // Test plumbing of volume controls and automatic gain controls.  Calls to
    248     // wcais_ methods should delegate directly to mock_vais_.
    249     EXPECT_CALL(*mock_vais_, GetVolume());
    250     double volume = wcais_->GetVolume();
    251     EXPECT_CALL(*mock_vais_, GetMaxVolume());
    252     const double max_volume = wcais_->GetMaxVolume();
    253     volume *= 2.0;
    254     if (volume < max_volume) {
    255       volume = max_volume;
    256     }
    257     EXPECT_CALL(*mock_vais_, SetVolume(volume));
    258     wcais_->SetVolume(volume);
    259     EXPECT_CALL(*mock_vais_, GetAutomaticGainControl());
    260     bool auto_gain = wcais_->GetAutomaticGainControl();
    261     auto_gain = !auto_gain;
    262     EXPECT_CALL(*mock_vais_, SetAutomaticGainControl(auto_gain));
    263     wcais_->SetAutomaticGainControl(auto_gain);
    264   }
    265 
    266   void AddAnotherInput() {
    267     // Note: WCAIS posts a task to invoke
    268     // MockAudioMirroringManager::StartMirroring() on the IO thread, which
    269     // causes our mock to set |destination_|.  Block until that has happened.
    270     base::WaitableEvent done(false, false);
    271     BrowserThread::PostTask(
    272         BrowserThread::IO, FROM_HERE, base::Bind(
    273             &base::WaitableEvent::Signal, base::Unretained(&done)));
    274     done.Wait();
    275     ASSERT_TRUE(destination_);
    276 
    277     EXPECT_CALL(*mock_vais_, AddOutputStream(NotNull(), _))
    278         .RetiresOnSaturation();
    279     // Later, when stream is closed:
    280     EXPECT_CALL(*mock_vais_, RemoveOutputStream(NotNull(), _))
    281         .RetiresOnSaturation();
    282 
    283     const AudioParameters& params = TestAudioParameters();
    284     AudioOutputStream* const out = destination_->AddInput(params);
    285     ASSERT_TRUE(out);
    286     streams_.push_back(out);
    287     EXPECT_TRUE(out->Open());
    288     SineWaveAudioSource* const source = new SineWaveAudioSource(
    289         params.channel_layout(), 200.0, params.sample_rate());
    290     sources_.push_back(source);
    291     out->Start(source);
    292   }
    293 
    294   void RemoveOneInputInFIFOOrder() {
    295     ASSERT_FALSE(streams_.empty());
    296     AudioOutputStream* const out = streams_.front();
    297     streams_.pop_front();
    298     out->Stop();
    299     out->Close();  // Self-deletes.
    300     ASSERT_TRUE(!sources_.empty());
    301     delete sources_.front();
    302     sources_.pop_front();
    303   }
    304 
    305   void ChangeMirroringTarget() {
    306     const int next_render_process_id =
    307         current_render_process_id_ == kRenderProcessId ?
    308             kAnotherRenderProcessId : kRenderProcessId;
    309     const int next_render_frame_id =
    310         current_render_frame_id_ == kRenderFrameId ?
    311             kAnotherRenderFrameId : kRenderFrameId;
    312 
    313     EXPECT_CALL(*mock_mirroring_manager_, StartMirroring(NotNull()))
    314         .WillOnce(SaveArg<0>(&destination_))
    315         .RetiresOnSaturation();
    316 
    317     SimulateChangeCallback(next_render_process_id, next_render_frame_id);
    318 
    319     current_render_process_id_ = next_render_process_id;
    320     current_render_frame_id_ = next_render_frame_id;
    321   }
    322 
    323   void LoseMirroringTarget() {
    324     EXPECT_CALL(mock_input_callback_, OnError(_));
    325 
    326     SimulateChangeCallback(-1, -1);
    327   }
    328 
    329   void Stop() {
    330     wcais_->Stop();
    331   }
    332 
    333   void Close() {
    334     // WebContentsAudioInputStream self-destructs on Close().  Its internal
    335     // objects hang around until they are no longer referred to (e.g., as tasks
    336     // on other threads shut things down).
    337     wcais_->Close();
    338     wcais_ = NULL;
    339     mock_vais_ = NULL;
    340   }
    341 
    342   void RunOnAudioThread(const base::Closure& closure) {
    343     audio_thread_.message_loop()->PostTask(FROM_HERE, closure);
    344   }
    345 
    346   // Block the calling thread until OnData() callbacks are being made.
    347   void WaitForData() {
    348     // Note: Arbitrarily chosen, but more iterations causes tests to take
    349     // significantly more time.
    350     static const int kNumIterations = 3;
    351     for (int i = 0; i < kNumIterations; ++i)
    352       on_data_event_.Wait();
    353   }
    354 
    355  private:
    356   void SimulateChangeCallback(int render_process_id, int render_frame_id) {
    357     ASSERT_FALSE(change_callback_.is_null());
    358     if (render_process_id == -1 || render_frame_id == -1) {
    359       change_callback_.Run(NULL);
    360     } else {
    361       // For our tests, any non-NULL value will suffice since it will not be
    362       // dereferenced.
    363       change_callback_.Run(reinterpret_cast<RenderWidgetHost*>(0xdeadbee5));
    364     }
    365   }
    366 
    367   scoped_ptr<TestBrowserThreadBundle> thread_bundle_;
    368   base::Thread audio_thread_;
    369 
    370   scoped_ptr<MockAudioMirroringManager> mock_mirroring_manager_;
    371   scoped_refptr<MockWebContentsTracker> mock_tracker_;
    372 
    373   MockVirtualAudioInputStream* mock_vais_;  // Owned by wcais_.
    374   WebContentsAudioInputStream* wcais_;  // Self-destructs on Close().
    375 
    376   // Mock consumer of audio data.
    377   MockAudioInputCallback mock_input_callback_;
    378 
    379   // Provided by WebContentsAudioInputStream to the mock WebContentsTracker.
    380   // This callback is saved here, and test code will invoke it to simulate
    381   // target change events.
    382   WebContentsTracker::ChangeCallback change_callback_;
    383 
    384   // Provided by WebContentsAudioInputStream to the mock AudioMirroringManager.
    385   // A pointer to the implementation is saved here, and test code will invoke it
    386   // to simulate: 1) calls to AddInput(); and 2) diverting audio data.
    387   AudioMirroringManager::MirroringDestination* destination_;
    388 
    389   // Current target RenderFrame.  These get flipped in ChangedMirroringTarget().
    390   int current_render_process_id_;
    391   int current_render_frame_id_;
    392 
    393   // Streams provided by calls to WebContentsAudioInputStream::AddInput().  Each
    394   // is started with a simulated source of audio data.
    395   std::list<AudioOutputStream*> streams_;
    396   std::list<SineWaveAudioSource*> sources_;  // 1:1 with elements in streams_.
    397 
    398   base::WaitableEvent on_data_event_;
    399 
    400   DISALLOW_COPY_AND_ASSIGN(WebContentsAudioInputStreamTest);
    401 };
    402 
    403 #define RUN_ON_AUDIO_THREAD(method) \
    404   RunOnAudioThread(base::Bind(&WebContentsAudioInputStreamTest::method,  \
    405                               base::Unretained(this)))
    406 
    407 TEST_F(WebContentsAudioInputStreamTest, OpenedButNeverStarted) {
    408   RUN_ON_AUDIO_THREAD(Open);
    409   RUN_ON_AUDIO_THREAD(Close);
    410 }
    411 
    412 TEST_F(WebContentsAudioInputStreamTest, MirroringNothing) {
    413   RUN_ON_AUDIO_THREAD(Open);
    414   RUN_ON_AUDIO_THREAD(Start);
    415   WaitForData();
    416   RUN_ON_AUDIO_THREAD(Stop);
    417   RUN_ON_AUDIO_THREAD(Close);
    418 }
    419 
    420 TEST_F(WebContentsAudioInputStreamTest, MirroringOutputOutlivesSession) {
    421   RUN_ON_AUDIO_THREAD(Open);
    422   RUN_ON_AUDIO_THREAD(Start);
    423   RUN_ON_AUDIO_THREAD(AddAnotherInput);
    424   WaitForData();
    425   RUN_ON_AUDIO_THREAD(Stop);
    426   RUN_ON_AUDIO_THREAD(Close);
    427   RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
    428 }
    429 
    430 TEST_F(WebContentsAudioInputStreamTest, MirroringOutputWithinSession) {
    431   RUN_ON_AUDIO_THREAD(Open);
    432   RUN_ON_AUDIO_THREAD(Start);
    433   RUN_ON_AUDIO_THREAD(AddAnotherInput);
    434   WaitForData();
    435   RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
    436   RUN_ON_AUDIO_THREAD(Stop);
    437   RUN_ON_AUDIO_THREAD(Close);
    438 }
    439 
    440 TEST_F(WebContentsAudioInputStreamTest, MirroringNothingWithTargetChange) {
    441   RUN_ON_AUDIO_THREAD(Open);
    442   RUN_ON_AUDIO_THREAD(Start);
    443   RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
    444   RUN_ON_AUDIO_THREAD(Stop);
    445   RUN_ON_AUDIO_THREAD(Close);
    446 }
    447 
    448 TEST_F(WebContentsAudioInputStreamTest, MirroringOneStreamAfterTargetChange) {
    449   RUN_ON_AUDIO_THREAD(Open);
    450   RUN_ON_AUDIO_THREAD(Start);
    451   RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
    452   RUN_ON_AUDIO_THREAD(AddAnotherInput);
    453   WaitForData();
    454   RUN_ON_AUDIO_THREAD(Stop);
    455   RUN_ON_AUDIO_THREAD(Close);
    456   RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
    457 }
    458 
    459 TEST_F(WebContentsAudioInputStreamTest, MirroringOneStreamWithTargetChange) {
    460   RUN_ON_AUDIO_THREAD(Open);
    461   RUN_ON_AUDIO_THREAD(Start);
    462   RUN_ON_AUDIO_THREAD(AddAnotherInput);
    463   WaitForData();
    464   RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
    465   RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
    466   RUN_ON_AUDIO_THREAD(AddAnotherInput);
    467   WaitForData();
    468   RUN_ON_AUDIO_THREAD(Stop);
    469   RUN_ON_AUDIO_THREAD(Close);
    470   RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
    471 }
    472 
    473 TEST_F(WebContentsAudioInputStreamTest, MirroringLostTarget) {
    474   RUN_ON_AUDIO_THREAD(Open);
    475   RUN_ON_AUDIO_THREAD(Start);
    476   RUN_ON_AUDIO_THREAD(AddAnotherInput);
    477   WaitForData();
    478   RUN_ON_AUDIO_THREAD(LoseMirroringTarget);
    479   RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
    480   RUN_ON_AUDIO_THREAD(Stop);
    481   RUN_ON_AUDIO_THREAD(Close);
    482 }
    483 
    484 TEST_F(WebContentsAudioInputStreamTest, MirroringMultipleStreamsAndTargets) {
    485   RUN_ON_AUDIO_THREAD(Open);
    486   RUN_ON_AUDIO_THREAD(Start);
    487   RUN_ON_AUDIO_THREAD(AddAnotherInput);
    488   WaitForData();
    489   RUN_ON_AUDIO_THREAD(AddAnotherInput);
    490   RUN_ON_AUDIO_THREAD(AddAnotherInput);
    491   RUN_ON_AUDIO_THREAD(AddAnotherInput);
    492   WaitForData();
    493   RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
    494   RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
    495   WaitForData();
    496   RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
    497   RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
    498   RUN_ON_AUDIO_THREAD(AddAnotherInput);
    499   WaitForData();
    500   RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
    501   WaitForData();
    502   RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
    503   RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
    504   RUN_ON_AUDIO_THREAD(Stop);
    505   RUN_ON_AUDIO_THREAD(Close);
    506 }
    507 
    508 }  // namespace content
    509