| Index: content/browser/renderer_host/media/web_contents_audio_input_stream_unittest.cc
|
| diff --git a/content/browser/renderer_host/media/web_contents_audio_input_stream_unittest.cc b/content/browser/renderer_host/media/web_contents_audio_input_stream_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e226687ca4aaa9794921f15597a519800d99ec70
|
| --- /dev/null
|
| +++ b/content/browser/renderer_host/media/web_contents_audio_input_stream_unittest.cc
|
| @@ -0,0 +1,512 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "content/browser/renderer_host/media/web_contents_audio_input_stream.h"
|
| +
|
| +#include <list>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/bind_helpers.h"
|
| +#include "base/message_loop.h"
|
| +#include "base/synchronization/waitable_event.h"
|
| +#include "base/threading/thread.h"
|
| +#include "content/browser/browser_thread_impl.h"
|
| +#include "content/browser/renderer_host/media/audio_mirroring_manager.h"
|
| +#include "content/browser/renderer_host/media/web_contents_tracker.h"
|
| +#include "media/audio/simple_sources.h"
|
| +#include "media/audio/virtual_audio_input_stream.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +using ::testing::_;
|
| +using ::testing::Assign;
|
| +using ::testing::DoAll;
|
| +using ::testing::Invoke;
|
| +using ::testing::InvokeWithoutArgs;
|
| +using ::testing::NotNull;
|
| +using ::testing::SaveArg;
|
| +using ::testing::WithArgs;
|
| +
|
| +using media::AudioInputStream;
|
| +using media::AudioOutputStream;
|
| +using media::AudioParameters;
|
| +using media::SineWaveAudioSource;
|
| +using media::VirtualAudioInputStream;
|
| +using media::VirtualAudioOutputStream;
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +const int kRenderProcessId = 123;
|
| +const int kRenderViewId = 456;
|
| +const int kAnotherRenderProcessId = 789;
|
| +const int kAnotherRenderViewId = 1;
|
| +
|
| +const AudioParameters& TestAudioParameters() {
|
| + static const AudioParameters params(
|
| + AudioParameters::AUDIO_FAKE,
|
| + media::CHANNEL_LAYOUT_STEREO,
|
| + AudioParameters::kAudioCDSampleRate, 16,
|
| + AudioParameters::kAudioCDSampleRate / 100);
|
| + return params;
|
| +}
|
| +
|
| +class MockAudioMirroringManager : public AudioMirroringManager {
|
| + public:
|
| + MockAudioMirroringManager() : AudioMirroringManager() {}
|
| + virtual ~MockAudioMirroringManager() {}
|
| +
|
| + MOCK_METHOD3(StartMirroring,
|
| + void(int render_process_id, int render_view_id,
|
| + MirroringDestination* destination));
|
| + MOCK_METHOD3(StopMirroring,
|
| + void(int render_process_id, int render_view_id,
|
| + MirroringDestination* destination));
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(MockAudioMirroringManager);
|
| +};
|
| +
|
| +class MockWebContentsTracker : public WebContentsTracker {
|
| + public:
|
| + MockWebContentsTracker() : WebContentsTracker() {}
|
| +
|
| + MOCK_METHOD3(Start,
|
| + void(int render_process_id, int render_view_id,
|
| + const ChangeCallback& callback));
|
| + MOCK_METHOD0(Stop, void());
|
| +
|
| + private:
|
| + virtual ~MockWebContentsTracker() {}
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(MockWebContentsTracker);
|
| +};
|
| +
|
| +// A fully-functional VirtualAudioInputStream, but methods are mocked to allow
|
| +// tests to check how/when they are invoked.
|
| +class MockVirtualAudioInputStream : public VirtualAudioInputStream {
|
| + public:
|
| + explicit MockVirtualAudioInputStream(base::MessageLoopProxy* message_loop)
|
| + : VirtualAudioInputStream(TestAudioParameters(), message_loop,
|
| + VirtualAudioInputStream::AfterCloseCallback()),
|
| + real_(TestAudioParameters(), message_loop,
|
| + base::Bind(&MockVirtualAudioInputStream::OnRealStreamHasClosed,
|
| + base::Unretained(this))),
|
| + real_stream_is_closed_(false) {
|
| + // Set default actions of mocked methods to delegate to the concrete
|
| + // implementation.
|
| + ON_CALL(*this, Open())
|
| + .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Open));
|
| + ON_CALL(*this, Start(_))
|
| + .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Start));
|
| + ON_CALL(*this, Stop())
|
| + .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Stop));
|
| + ON_CALL(*this, Close())
|
| + .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::Close));
|
| + ON_CALL(*this, GetMaxVolume())
|
| + .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::GetMaxVolume));
|
| + ON_CALL(*this, SetVolume(_))
|
| + .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::SetVolume));
|
| + ON_CALL(*this, GetVolume())
|
| + .WillByDefault(Invoke(&real_, &VirtualAudioInputStream::GetVolume));
|
| + ON_CALL(*this, SetAutomaticGainControl(_))
|
| + .WillByDefault(
|
| + Invoke(&real_, &VirtualAudioInputStream::SetAutomaticGainControl));
|
| + ON_CALL(*this, GetAutomaticGainControl())
|
| + .WillByDefault(
|
| + Invoke(&real_, &VirtualAudioInputStream::GetAutomaticGainControl));
|
| + ON_CALL(*this, AddOutputStream(NotNull(), _))
|
| + .WillByDefault(
|
| + Invoke(&real_, &VirtualAudioInputStream::AddOutputStream));
|
| + ON_CALL(*this, RemoveOutputStream(NotNull(), _))
|
| + .WillByDefault(
|
| + Invoke(&real_, &VirtualAudioInputStream::RemoveOutputStream));
|
| + }
|
| +
|
| + ~MockVirtualAudioInputStream() {
|
| + DCHECK(real_stream_is_closed_);
|
| + }
|
| +
|
| + MOCK_METHOD0(Open, bool());
|
| + MOCK_METHOD1(Start, void(AudioInputStream::AudioInputCallback*));
|
| + MOCK_METHOD0(Stop, void());
|
| + MOCK_METHOD0(Close, void());
|
| + MOCK_METHOD0(GetMaxVolume, double());
|
| + MOCK_METHOD1(SetVolume, void(double));
|
| + MOCK_METHOD0(GetVolume, double());
|
| + MOCK_METHOD1(SetAutomaticGainControl, void(bool));
|
| + MOCK_METHOD0(GetAutomaticGainControl, bool());
|
| + MOCK_METHOD2(AddOutputStream, void(VirtualAudioOutputStream*,
|
| + const AudioParameters&));
|
| + MOCK_METHOD2(RemoveOutputStream, void(VirtualAudioOutputStream*,
|
| + const AudioParameters&));
|
| +
|
| + private:
|
| + void OnRealStreamHasClosed(VirtualAudioInputStream* stream) {
|
| + DCHECK_EQ(&real_, stream);
|
| + DCHECK(!real_stream_is_closed_);
|
| + real_stream_is_closed_ = true;
|
| + }
|
| +
|
| + VirtualAudioInputStream real_;
|
| + bool real_stream_is_closed_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(MockVirtualAudioInputStream);
|
| +};
|
| +
|
| +class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
|
| + public:
|
| + MockAudioInputCallback() {}
|
| +
|
| + MOCK_METHOD5(OnData, void(AudioInputStream* stream, const uint8* src,
|
| + uint32 size, uint32 hardware_delay_bytes,
|
| + double volume));
|
| + MOCK_METHOD1(OnClose, void(AudioInputStream* stream));
|
| + MOCK_METHOD2(OnError, void(AudioInputStream* stream, int code));
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(MockAudioInputCallback);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +class WebContentsAudioInputStreamTest : public testing::Test {
|
| + public:
|
| + WebContentsAudioInputStreamTest()
|
| + : audio_thread_("Audio thread"),
|
| + io_thread_(BrowserThread::IO),
|
| + mock_mirroring_manager_(new MockAudioMirroringManager()),
|
| + mock_tracker_(new MockWebContentsTracker()),
|
| + mock_vais_(NULL),
|
| + wcais_(NULL),
|
| + destination_(NULL),
|
| + current_render_process_id_(kRenderProcessId),
|
| + current_render_view_id_(kRenderViewId),
|
| + on_data_event_(false, false) {
|
| + audio_thread_.Start();
|
| + io_thread_.Start();
|
| + }
|
| +
|
| + virtual ~WebContentsAudioInputStreamTest() {
|
| + audio_thread_.Stop();
|
| + io_thread_.Stop();
|
| +
|
| + DCHECK(!mock_vais_);
|
| + DCHECK(!wcais_);
|
| + EXPECT_FALSE(destination_);
|
| + DCHECK(streams_.empty());
|
| + DCHECK(sources_.empty());
|
| + }
|
| +
|
| + void Open() {
|
| + mock_vais_ =
|
| + new MockVirtualAudioInputStream(audio_thread_.message_loop_proxy());
|
| + EXPECT_CALL(*mock_vais_, Open());
|
| + EXPECT_CALL(*mock_vais_, Close()); // At Close() time.
|
| +
|
| + ASSERT_EQ(kRenderProcessId, current_render_process_id_);
|
| + ASSERT_EQ(kRenderViewId, current_render_view_id_);
|
| + EXPECT_CALL(*mock_tracker_, Start(kRenderProcessId, kRenderViewId, _))
|
| + .WillOnce(DoAll(
|
| + SaveArg<2>(&change_callback_),
|
| + WithArgs<0, 1>(
|
| + Invoke(&change_callback_,
|
| + &WebContentsTracker::ChangeCallback::Run))));
|
| + EXPECT_CALL(*mock_tracker_, Stop()); // At Close() time.
|
| +
|
| + wcais_ = new WebContentsAudioInputStream(
|
| + current_render_process_id_, current_render_view_id_,
|
| + audio_thread_.message_loop_proxy(), mock_mirroring_manager_.get(),
|
| + mock_tracker_, mock_vais_);
|
| + wcais_->Open();
|
| + }
|
| +
|
| + void Start() {
|
| + EXPECT_CALL(*mock_vais_, Start(&mock_input_callback_));
|
| + EXPECT_CALL(*mock_vais_, Stop()); // At Stop() time.
|
| +
|
| + EXPECT_CALL(*mock_mirroring_manager_,
|
| + StartMirroring(kRenderProcessId, kRenderViewId, NotNull()))
|
| + .WillOnce(SaveArg<2>(&destination_))
|
| + .RetiresOnSaturation();
|
| + // At Stop() time, or when the mirroring target changes:
|
| + EXPECT_CALL(*mock_mirroring_manager_,
|
| + StopMirroring(kRenderProcessId, kRenderViewId, NotNull()))
|
| + .WillOnce(Assign(
|
| + &destination_,
|
| + static_cast<AudioMirroringManager::MirroringDestination*>(NULL)))
|
| + .RetiresOnSaturation();
|
| +
|
| + EXPECT_CALL(mock_input_callback_, OnData(NotNull(), NotNull(), _, _, _))
|
| + .WillRepeatedly(
|
| + InvokeWithoutArgs(&on_data_event_, &base::WaitableEvent::Signal));
|
| + EXPECT_CALL(mock_input_callback_, OnClose(_)); // At Stop() time.
|
| +
|
| + wcais_->Start(&mock_input_callback_);
|
| +
|
| + // Test plumbing of volume controls and automatic gain controls. Calls to
|
| + // wcais_ methods should delegate directly to mock_vais_.
|
| + EXPECT_CALL(*mock_vais_, GetVolume());
|
| + double volume = wcais_->GetVolume();
|
| + EXPECT_CALL(*mock_vais_, GetMaxVolume());
|
| + const double max_volume = wcais_->GetMaxVolume();
|
| + volume *= 2.0;
|
| + if (volume < max_volume) {
|
| + volume = max_volume;
|
| + }
|
| + EXPECT_CALL(*mock_vais_, SetVolume(volume));
|
| + wcais_->SetVolume(volume);
|
| + EXPECT_CALL(*mock_vais_, GetAutomaticGainControl());
|
| + bool auto_gain = wcais_->GetAutomaticGainControl();
|
| + auto_gain = !auto_gain;
|
| + EXPECT_CALL(*mock_vais_, SetAutomaticGainControl(auto_gain));
|
| + wcais_->SetAutomaticGainControl(auto_gain);
|
| + }
|
| +
|
| + void AddAnotherInput() {
|
| + // Note: WCAIS posts a task to invoke
|
| + // MockAudioMirroringManager::StartMirroring() on the IO thread, which
|
| + // causes our mock to set |destination_|. Block until that has happened.
|
| + base::WaitableEvent done(false, false);
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO, FROM_HERE, base::Bind(
|
| + &base::WaitableEvent::Signal, base::Unretained(&done)));
|
| + done.Wait();
|
| + ASSERT_TRUE(destination_);
|
| +
|
| + EXPECT_CALL(*mock_vais_, AddOutputStream(NotNull(), _))
|
| + .RetiresOnSaturation();
|
| + // Later, when stream is closed:
|
| + EXPECT_CALL(*mock_vais_, RemoveOutputStream(NotNull(), _))
|
| + .RetiresOnSaturation();
|
| +
|
| + const AudioParameters& params = TestAudioParameters();
|
| + AudioOutputStream* const out = destination_->AddInput(params);
|
| + ASSERT_TRUE(out);
|
| + streams_.push_back(out);
|
| + EXPECT_TRUE(out->Open());
|
| + SineWaveAudioSource* const source = new SineWaveAudioSource(
|
| + params.channel_layout(), 200.0, params.sample_rate());
|
| + sources_.push_back(source);
|
| + out->Start(source);
|
| + }
|
| +
|
| + void RemoveOneInputInFIFOOrder() {
|
| + ASSERT_FALSE(streams_.empty());
|
| + AudioOutputStream* const out = streams_.front();
|
| + streams_.pop_front();
|
| + out->Stop();
|
| + out->Close(); // Self-deletes.
|
| + ASSERT_TRUE(!sources_.empty());
|
| + delete sources_.front();
|
| + sources_.pop_front();
|
| + }
|
| +
|
| + void ChangeMirroringTarget() {
|
| + const int next_render_process_id =
|
| + current_render_process_id_ == kRenderProcessId ?
|
| + kAnotherRenderProcessId : kRenderProcessId;
|
| + const int next_render_view_id =
|
| + current_render_view_id_ == kRenderViewId ?
|
| + kAnotherRenderViewId : kRenderViewId;
|
| +
|
| + EXPECT_CALL(*mock_mirroring_manager_,
|
| + StartMirroring(next_render_process_id, next_render_view_id,
|
| + NotNull()))
|
| + .WillOnce(SaveArg<2>(&destination_))
|
| + .RetiresOnSaturation();
|
| + // At Stop() time, or when the mirroring target changes:
|
| + EXPECT_CALL(*mock_mirroring_manager_,
|
| + StopMirroring(next_render_process_id, next_render_view_id,
|
| + NotNull()))
|
| + .WillOnce(Assign(
|
| + &destination_,
|
| + static_cast<AudioMirroringManager::MirroringDestination*>(NULL)))
|
| + .RetiresOnSaturation();
|
| +
|
| + // Simulate OnTargetChange() callback from WebContentsTracker.
|
| + EXPECT_FALSE(change_callback_.is_null());
|
| + change_callback_.Run(next_render_process_id, next_render_view_id);
|
| +
|
| + current_render_process_id_ = next_render_process_id;
|
| + current_render_view_id_ = next_render_view_id;
|
| + }
|
| +
|
| + void LoseMirroringTarget() {
|
| + EXPECT_CALL(mock_input_callback_, OnError(_, _));
|
| +
|
| + // Simulate OnTargetChange() callback from WebContentsTracker.
|
| + EXPECT_FALSE(change_callback_.is_null());
|
| + change_callback_.Run(-1, -1);
|
| + }
|
| +
|
| + void Stop() {
|
| + wcais_->Stop();
|
| + }
|
| +
|
| + void Close() {
|
| + // WebContentsAudioInputStream self-destructs on Close(). Its internal
|
| + // objects hang around until they are no longer referred to (e.g., as tasks
|
| + // on other threads shut things down).
|
| + wcais_->Close();
|
| + wcais_ = NULL;
|
| + mock_vais_ = NULL;
|
| + }
|
| +
|
| + void RunOnAudioThread(const base::Closure& closure) {
|
| + audio_thread_.message_loop()->PostTask(FROM_HERE, closure);
|
| + }
|
| +
|
| + // Block the calling thread until OnData() callbacks are being made.
|
| + void WaitForData() {
|
| + // Note: Arbitrarily chosen, but more iterations causes tests to take
|
| + // significantly more time.
|
| + static const int kNumIterations = 3;
|
| + for (int i = 0; i < kNumIterations; ++i)
|
| + on_data_event_.Wait();
|
| + }
|
| +
|
| + private:
|
| + base::Thread audio_thread_;
|
| + BrowserThreadImpl io_thread_;
|
| +
|
| + scoped_ptr<MockAudioMirroringManager> mock_mirroring_manager_;
|
| + scoped_refptr<MockWebContentsTracker> mock_tracker_;
|
| +
|
| + MockVirtualAudioInputStream* mock_vais_; // Owned by wcais_.
|
| + WebContentsAudioInputStream* wcais_; // Self-destructs on Close().
|
| +
|
| + // Mock consumer of audio data.
|
| + MockAudioInputCallback mock_input_callback_;
|
| +
|
| + // Provided by WebContentsAudioInputStream to the mock WebContentsTracker.
|
| + // This callback is saved here, and test code will invoke it to simulate
|
| + // target change events.
|
| + WebContentsTracker::ChangeCallback change_callback_;
|
| +
|
| + // Provided by WebContentsAudioInputStream to the mock AudioMirroringManager.
|
| + // A pointer to the implementation is saved here, and test code will invoke it
|
| + // to simulate: 1) calls to AddInput(); and 2) diverting audio data.
|
| + AudioMirroringManager::MirroringDestination* destination_;
|
| +
|
| + // Current target RenderView. These get flipped in ChangedMirroringTarget().
|
| + int current_render_process_id_;
|
| + int current_render_view_id_;
|
| +
|
| + // Streams provided by calls to WebContentsAudioInputStream::AddInput(). Each
|
| + // is started with a simulated source of audio data.
|
| + std::list<AudioOutputStream*> streams_;
|
| + std::list<SineWaveAudioSource*> sources_; // 1:1 with elements in streams_.
|
| +
|
| + base::WaitableEvent on_data_event_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(WebContentsAudioInputStreamTest);
|
| +};
|
| +
|
| +#define RUN_ON_AUDIO_THREAD(method) \
|
| + RunOnAudioThread(base::Bind(&WebContentsAudioInputStreamTest::method, \
|
| + base::Unretained(this)))
|
| +
|
| +TEST_F(WebContentsAudioInputStreamTest, OpenedButNeverStarted) {
|
| + RUN_ON_AUDIO_THREAD(Open);
|
| + RUN_ON_AUDIO_THREAD(Close);
|
| +}
|
| +
|
| +TEST_F(WebContentsAudioInputStreamTest, MirroringNothing) {
|
| + RUN_ON_AUDIO_THREAD(Open);
|
| + RUN_ON_AUDIO_THREAD(Start);
|
| + RUN_ON_AUDIO_THREAD(Stop);
|
| + RUN_ON_AUDIO_THREAD(Close);
|
| +}
|
| +
|
| +TEST_F(WebContentsAudioInputStreamTest, MirroringOutputOutlivesSession) {
|
| + RUN_ON_AUDIO_THREAD(Open);
|
| + RUN_ON_AUDIO_THREAD(Start);
|
| + RUN_ON_AUDIO_THREAD(AddAnotherInput);
|
| + WaitForData();
|
| + RUN_ON_AUDIO_THREAD(Stop);
|
| + RUN_ON_AUDIO_THREAD(Close);
|
| + RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
|
| +}
|
| +
|
| +TEST_F(WebContentsAudioInputStreamTest, MirroringOutputWithinSession) {
|
| + RUN_ON_AUDIO_THREAD(Open);
|
| + RUN_ON_AUDIO_THREAD(Start);
|
| + RUN_ON_AUDIO_THREAD(AddAnotherInput);
|
| + WaitForData();
|
| + RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
|
| + RUN_ON_AUDIO_THREAD(Stop);
|
| + RUN_ON_AUDIO_THREAD(Close);
|
| +}
|
| +
|
| +TEST_F(WebContentsAudioInputStreamTest, MirroringNothingWithTargetChange) {
|
| + RUN_ON_AUDIO_THREAD(Open);
|
| + RUN_ON_AUDIO_THREAD(Start);
|
| + RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
|
| + RUN_ON_AUDIO_THREAD(Stop);
|
| + RUN_ON_AUDIO_THREAD(Close);
|
| +}
|
| +
|
| +TEST_F(WebContentsAudioInputStreamTest, MirroringOneStreamAfterTargetChange) {
|
| + RUN_ON_AUDIO_THREAD(Open);
|
| + RUN_ON_AUDIO_THREAD(Start);
|
| + RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
|
| + RUN_ON_AUDIO_THREAD(AddAnotherInput);
|
| + WaitForData();
|
| + RUN_ON_AUDIO_THREAD(Stop);
|
| + RUN_ON_AUDIO_THREAD(Close);
|
| + RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
|
| +}
|
| +
|
| +TEST_F(WebContentsAudioInputStreamTest, MirroringOneStreamWithTargetChange) {
|
| + RUN_ON_AUDIO_THREAD(Open);
|
| + RUN_ON_AUDIO_THREAD(Start);
|
| + RUN_ON_AUDIO_THREAD(AddAnotherInput);
|
| + WaitForData();
|
| + RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
|
| + RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
|
| + RUN_ON_AUDIO_THREAD(AddAnotherInput);
|
| + WaitForData();
|
| + RUN_ON_AUDIO_THREAD(Stop);
|
| + RUN_ON_AUDIO_THREAD(Close);
|
| + RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
|
| +}
|
| +
|
| +TEST_F(WebContentsAudioInputStreamTest, MirroringLostTarget) {
|
| + RUN_ON_AUDIO_THREAD(Open);
|
| + RUN_ON_AUDIO_THREAD(Start);
|
| + RUN_ON_AUDIO_THREAD(AddAnotherInput);
|
| + WaitForData();
|
| + RUN_ON_AUDIO_THREAD(LoseMirroringTarget);
|
| + RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
|
| + RUN_ON_AUDIO_THREAD(Stop);
|
| + RUN_ON_AUDIO_THREAD(Close);
|
| +}
|
| +
|
| +TEST_F(WebContentsAudioInputStreamTest, MirroringMultipleStreamsAndTargets) {
|
| + RUN_ON_AUDIO_THREAD(Open);
|
| + RUN_ON_AUDIO_THREAD(Start);
|
| + RUN_ON_AUDIO_THREAD(AddAnotherInput);
|
| + WaitForData();
|
| + RUN_ON_AUDIO_THREAD(AddAnotherInput);
|
| + RUN_ON_AUDIO_THREAD(AddAnotherInput);
|
| + RUN_ON_AUDIO_THREAD(AddAnotherInput);
|
| + WaitForData();
|
| + RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
|
| + RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
|
| + WaitForData();
|
| + RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
|
| + RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
|
| + RUN_ON_AUDIO_THREAD(AddAnotherInput);
|
| + WaitForData();
|
| + RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
|
| + WaitForData();
|
| + RUN_ON_AUDIO_THREAD(ChangeMirroringTarget);
|
| + RUN_ON_AUDIO_THREAD(RemoveOneInputInFIFOOrder);
|
| + RUN_ON_AUDIO_THREAD(Stop);
|
| + RUN_ON_AUDIO_THREAD(Close);
|
| +}
|
| +
|
| +} // namespace content
|
|
|