| Index: content/browser/renderer_host/media/renderer_audio_output_stream_factory_context_impl_unittest.cc
|
| diff --git a/content/browser/renderer_host/media/renderer_audio_output_stream_factory_context_impl_unittest.cc b/content/browser/renderer_host/media/renderer_audio_output_stream_factory_context_impl_unittest.cc
|
| deleted file mode 100644
|
| index 4a81273b7d168dc542e0543b494062594dbdf772..0000000000000000000000000000000000000000
|
| --- a/content/browser/renderer_host/media/renderer_audio_output_stream_factory_context_impl_unittest.cc
|
| +++ /dev/null
|
| @@ -1,383 +0,0 @@
|
| -// Copyright 2017 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/renderer_audio_output_stream_factory_context_impl.h"
|
| -
|
| -#include <utility>
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/memory/shared_memory.h"
|
| -#include "base/memory/shared_memory_handle.h"
|
| -#include "base/run_loop.h"
|
| -#include "base/sync_socket.h"
|
| -#include "cc/base/math_util.h"
|
| -#include "content/browser/audio_manager_thread.h"
|
| -#include "content/browser/renderer_host/media/media_stream_manager.h"
|
| -#include "content/common/media/renderer_audio_output_stream_factory.mojom.h"
|
| -#include "content/public/browser/browser_thread.h"
|
| -#include "content/public/test/mock_render_process_host.h"
|
| -#include "content/public/test/test_browser_context.h"
|
| -#include "content/public/test/test_browser_thread_bundle.h"
|
| -#include "media/audio/audio_manager_base.h"
|
| -#include "media/audio/audio_output_controller.h"
|
| -#include "media/audio/audio_system_impl.h"
|
| -#include "media/audio/fake_audio_log_factory.h"
|
| -#include "media/audio/simple_sources.h"
|
| -#include "media/base/audio_parameters.h"
|
| -#include "media/base/media_switches.h"
|
| -#include "mojo/public/cpp/bindings/binding.h"
|
| -#include "mojo/public/cpp/system/platform_handle.h"
|
| -#include "testing/gmock/include/gmock/gmock.h"
|
| -#include "testing/gtest/include/gtest/gtest.h"
|
| -
|
| -namespace content {
|
| -
|
| -namespace {
|
| -
|
| -using testing::_;
|
| -using testing::StrictMock;
|
| -using testing::Return;
|
| -using testing::Test;
|
| -using AudioOutputStreamFactory = mojom::RendererAudioOutputStreamFactory;
|
| -using AudioOutputStreamFactoryPtr =
|
| - mojo::InterfacePtr<AudioOutputStreamFactory>;
|
| -using AudioOutputStreamFactoryRequest =
|
| - mojo::InterfaceRequest<AudioOutputStreamFactory>;
|
| -using AudioOutputStream = media::mojom::AudioOutputStream;
|
| -using AudioOutputStreamPtr = mojo::InterfacePtr<AudioOutputStream>;
|
| -using AudioOutputStreamRequest = mojo::InterfaceRequest<AudioOutputStream>;
|
| -using AudioOutputStreamProvider = media::mojom::AudioOutputStreamProvider;
|
| -using AudioOutputStreamProviderPtr =
|
| - mojo::InterfacePtr<AudioOutputStreamProvider>;
|
| -using AudioOutputStreamProviderRequest =
|
| - mojo::InterfaceRequest<AudioOutputStreamProvider>;
|
| -
|
| -const int kRenderProcessId = 42;
|
| -const int kRenderFrameId = 24;
|
| -const int kNoSessionId = 0;
|
| -const float kWaveFrequency = 440.f;
|
| -const int kChannels = 1;
|
| -const int kBuffers = 1000;
|
| -const int kSampleFrequency = 44100;
|
| -const int kBitsPerSample = 16;
|
| -const int kSamplesPerBuffer = kSampleFrequency / 100;
|
| -const char kSalt[] = "salt";
|
| -
|
| -std::unique_ptr<media::AudioOutputStream::AudioSourceCallback>
|
| -GetTestAudioSource() {
|
| - return base::MakeUnique<media::SineWaveAudioSource>(kChannels, kWaveFrequency,
|
| - kSampleFrequency);
|
| -}
|
| -
|
| -media::AudioParameters GetTestAudioParameters() {
|
| - return media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
|
| - media::CHANNEL_LAYOUT_MONO, kSampleFrequency,
|
| - kBitsPerSample, kSamplesPerBuffer);
|
| -}
|
| -
|
| -void SyncWith(scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
|
| - CHECK(task_runner);
|
| - CHECK(!task_runner->BelongsToCurrentThread());
|
| - base::WaitableEvent e = {base::WaitableEvent::ResetPolicy::MANUAL,
|
| - base::WaitableEvent::InitialState::NOT_SIGNALED};
|
| - task_runner->PostTask(FROM_HERE, base::Bind(&base::WaitableEvent::Signal,
|
| - base::Unretained(&e)));
|
| - e.Wait();
|
| -}
|
| -
|
| -void SyncWithAllThreads() {
|
| - DCHECK_CURRENTLY_ON(BrowserThread::UI);
|
| - // New tasks might be posted while we are syncing, but in every iteration at
|
| - // least one task will be run. 20 iterations should be enough for our code.
|
| - for (int i = 0; i < 20; ++i) {
|
| - {
|
| - base::MessageLoop::ScopedNestableTaskAllower allower(
|
| - base::MessageLoop::current());
|
| - base::RunLoop().RunUntilIdle();
|
| - }
|
| - SyncWith(BrowserThread::GetTaskRunnerForThread(BrowserThread::IO));
|
| - SyncWith(media::AudioManager::Get()->GetWorkerTaskRunner());
|
| - }
|
| -}
|
| -
|
| -class MockAudioManager : public media::AudioManagerBase {
|
| - public:
|
| - MockAudioManager(
|
| - scoped_refptr<base::SingleThreadTaskRunner> task_runner,
|
| - scoped_refptr<base::SingleThreadTaskRunner> worker_task_runner,
|
| - media::AudioLogFactory* audio_log_factory)
|
| - : media::AudioManagerBase(task_runner,
|
| - worker_task_runner,
|
| - audio_log_factory) {
|
| - ON_CALL(*this, HasAudioOutputDevices()).WillByDefault(Return(true));
|
| - }
|
| -
|
| - ~MockAudioManager() override { Shutdown(); }
|
| -
|
| - MOCK_METHOD0(HasAudioOutputDevices, bool());
|
| - MOCK_METHOD0(HasAudioInputDevices, bool());
|
| - MOCK_METHOD0(GetName, const char*());
|
| -
|
| - MOCK_METHOD2(MakeLinearOutputStream,
|
| - media::AudioOutputStream*(const media::AudioParameters& params,
|
| - const LogCallback& log_callback));
|
| - MOCK_METHOD3(MakeLowLatencyOutputStream,
|
| - media::AudioOutputStream*(const media::AudioParameters& params,
|
| - const std::string& device_id,
|
| - const LogCallback& log_callback));
|
| - MOCK_METHOD3(MakeLinearInputStream,
|
| - media::AudioInputStream*(const media::AudioParameters& params,
|
| - const std::string& device_id,
|
| - const LogCallback& log_callback));
|
| - MOCK_METHOD3(MakeLowLatencyInputStream,
|
| - media::AudioInputStream*(const media::AudioParameters& params,
|
| - const std::string& device_id,
|
| - const LogCallback& log_callback));
|
| - MOCK_METHOD2(GetPreferredOutputStreamParameters,
|
| - media::AudioParameters(const std::string& device_id,
|
| - const media::AudioParameters& params));
|
| -};
|
| -
|
| -class MockAudioOutputStream : public media::AudioOutputStream,
|
| - public base::PlatformThread::Delegate {
|
| - public:
|
| - explicit MockAudioOutputStream(MockAudioManager* audio_manager)
|
| - : done_(base::WaitableEvent::ResetPolicy::MANUAL,
|
| - base::WaitableEvent::InitialState::NOT_SIGNALED),
|
| - audio_manager_(audio_manager) {}
|
| -
|
| - ~MockAudioOutputStream() override {
|
| - base::PlatformThread::Join(thread_handle_);
|
| - }
|
| -
|
| - void Start(AudioSourceCallback* callback) override {
|
| - callback_ = callback;
|
| - EXPECT_TRUE(base::PlatformThread::CreateWithPriority(
|
| - 0, this, &thread_handle_, base::ThreadPriority::REALTIME_AUDIO));
|
| - }
|
| -
|
| - void Stop() override {
|
| - done_.Wait();
|
| - callback_ = nullptr;
|
| - }
|
| -
|
| - bool Open() override { return true; }
|
| - void SetVolume(double volume) override {}
|
| - void GetVolume(double* volume) override { *volume = 1; }
|
| - void Close() override {
|
| - Stop();
|
| - audio_manager_->ReleaseOutputStream(this);
|
| - }
|
| -
|
| - void ThreadMain() override {
|
| - std::unique_ptr<media::AudioOutputStream::AudioSourceCallback>
|
| - expected_audio = GetTestAudioSource();
|
| - media::AudioParameters params = GetTestAudioParameters();
|
| - std::unique_ptr<media::AudioBus> dest = media::AudioBus::Create(params);
|
| - std::unique_ptr<media::AudioBus> expected_buffer =
|
| - media::AudioBus::Create(params);
|
| - for (int i = 0; i < kBuffers; ++i) {
|
| - expected_audio->OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0,
|
| - expected_buffer.get());
|
| - callback_->OnMoreData(base::TimeDelta(), base::TimeTicks::Now(), 0,
|
| - dest.get());
|
| - for (int frame = 0; frame < params.frames_per_buffer(); ++frame) {
|
| - // Using EXPECT here causes massive log spam in case of a broken test,
|
| - // and ASSERT causes it to hang, so we use CHECK.
|
| - CHECK(cc::MathUtil::IsNearlyTheSameForTesting(
|
| - expected_buffer->channel(0)[frame], dest->channel(0)[frame]))
|
| - << "Got " << dest->channel(0)[frame] << ", expected "
|
| - << expected_buffer->channel(0)[frame];
|
| - }
|
| - }
|
| - done_.Signal();
|
| - }
|
| -
|
| - private:
|
| - base::OnceClosure sync_closure_;
|
| - base::PlatformThreadHandle thread_handle_;
|
| - base::WaitableEvent done_;
|
| - MockAudioManager* audio_manager_;
|
| - AudioSourceCallback* callback_;
|
| -};
|
| -
|
| -void AuthCallback(base::OnceClosure sync_closure,
|
| - media::OutputDeviceStatus* status_out,
|
| - media::AudioParameters* params_out,
|
| - std::string* id_out,
|
| - media::OutputDeviceStatus status,
|
| - const media::AudioParameters& params,
|
| - const std::string& id) {
|
| - *status_out = status;
|
| - *params_out = params;
|
| - *id_out = id;
|
| - std::move(sync_closure).Run();
|
| -}
|
| -
|
| -// "Renderer-side" audio client. Provides the signal given by
|
| -// GetTestAudioSource() from a dedicated thread when given sync socket and
|
| -// shared memory.
|
| -// TODO(maxmorin): Replace with an instance of the real client, when it exists.
|
| -class TestIPCClient : public base::PlatformThread::Delegate {
|
| - public:
|
| - TestIPCClient() {}
|
| -
|
| - ~TestIPCClient() override { base::PlatformThread::Join(thread_handle_); }
|
| -
|
| - // Starts thread, sets up IPC primitives and sends signal on thread.
|
| - void Start(mojo::ScopedSharedBufferHandle shared_buffer,
|
| - mojo::ScopedHandle socket_handle) {
|
| - EXPECT_TRUE(socket_handle.is_valid());
|
| - // Set up socket.
|
| - base::PlatformFile fd;
|
| - mojo::UnwrapPlatformFile(std::move(socket_handle), &fd);
|
| - socket_ = base::MakeUnique<base::CancelableSyncSocket>(fd);
|
| - EXPECT_NE(socket_->handle(), base::CancelableSyncSocket::kInvalidHandle);
|
| -
|
| - // Set up memory.
|
| - EXPECT_TRUE(shared_buffer.is_valid());
|
| - size_t memory_length;
|
| - base::SharedMemoryHandle shmem_handle;
|
| - bool read_only;
|
| - EXPECT_EQ(
|
| - mojo::UnwrapSharedMemoryHandle(std::move(shared_buffer), &shmem_handle,
|
| - &memory_length, &read_only),
|
| - MOJO_RESULT_OK);
|
| - EXPECT_EQ(memory_length, sizeof(media::AudioOutputBufferParameters) +
|
| - media::AudioBus::CalculateMemorySize(
|
| - GetTestAudioParameters()));
|
| - EXPECT_EQ(read_only, false);
|
| - memory_ = base::MakeUnique<base::SharedMemory>(shmem_handle, read_only);
|
| - EXPECT_TRUE(memory_->Map(memory_length));
|
| -
|
| - EXPECT_TRUE(base::PlatformThread::CreateWithPriority(
|
| - 0, this, &thread_handle_, base::ThreadPriority::REALTIME_AUDIO));
|
| - }
|
| -
|
| - void ThreadMain() override {
|
| - std::unique_ptr<media::AudioOutputStream::AudioSourceCallback>
|
| - audio_source = GetTestAudioSource();
|
| -
|
| - media::AudioOutputBuffer* buffer =
|
| - reinterpret_cast<media::AudioOutputBuffer*>(memory_->memory());
|
| - std::unique_ptr<media::AudioBus> output_bus =
|
| - media::AudioBus::WrapMemory(GetTestAudioParameters(), buffer->audio);
|
| -
|
| - // Send s.
|
| - for (uint32_t i = 0; i < kBuffers;) {
|
| - uint32_t pending_data = 0;
|
| - size_t bytes_read = socket_->Receive(&pending_data, sizeof(pending_data));
|
| - // Use check here, since there's a risk of hangs in case of a bug.
|
| - PCHECK(sizeof(pending_data) == bytes_read)
|
| - << "Tried to read " << sizeof(pending_data) << " bytes but only read "
|
| - << bytes_read << " bytes";
|
| - CHECK_EQ(0u, pending_data);
|
| -
|
| - ++i;
|
| - audio_source->OnMoreData(base::TimeDelta(), base::TimeTicks(), 0,
|
| - output_bus.get());
|
| -
|
| - size_t bytes_written = socket_->Send(&i, sizeof(i));
|
| - PCHECK(sizeof(pending_data) == bytes_written)
|
| - << "Tried to write " << sizeof(pending_data)
|
| - << " bytes but only wrote " << bytes_written << " bytes";
|
| - }
|
| - }
|
| -
|
| - private:
|
| - base::PlatformThreadHandle thread_handle_;
|
| - std::unique_ptr<base::CancelableSyncSocket> socket_;
|
| - std::unique_ptr<base::SharedMemory> memory_;
|
| -};
|
| -
|
| -} // namespace
|
| -
|
| -// TODO(maxmorin): Add test for play, pause and set volume.
|
| -class RendererAudioOutputStreamFactoryIntegrationTest : public Test {
|
| - public:
|
| - RendererAudioOutputStreamFactoryIntegrationTest()
|
| - : media_stream_manager_(),
|
| - thread_bundle_(TestBrowserThreadBundle::Options::REAL_IO_THREAD),
|
| - audio_thread_(),
|
| - log_factory_(),
|
| - audio_manager_(new MockAudioManager(audio_thread_.task_runner(),
|
| - audio_thread_.worker_task_runner(),
|
| - &log_factory_)),
|
| - audio_system_(media::AudioSystemImpl::Create(audio_manager_.get())) {
|
| - media_stream_manager_ =
|
| - base::MakeUnique<MediaStreamManager>(audio_system_.get());
|
| - }
|
| -
|
| - void CreateAndBindFactory(AudioOutputStreamFactoryRequest request) {
|
| - factory_context_.reset(new RendererAudioOutputStreamFactoryContextImpl(
|
| - kRenderProcessId, audio_system_.get(), audio_manager_.get(),
|
| - media_stream_manager_.get(), kSalt));
|
| - factory_context_->CreateFactory(kRenderFrameId, std::move(request));
|
| - }
|
| -
|
| - std::unique_ptr<MediaStreamManager> media_stream_manager_;
|
| - TestBrowserThreadBundle thread_bundle_;
|
| - AudioManagerThread audio_thread_;
|
| - media::FakeAudioLogFactory log_factory_;
|
| - media::ScopedAudioManagerPtr audio_manager_;
|
| - std::unique_ptr<media::AudioSystem> audio_system_;
|
| - std::unique_ptr<RendererAudioOutputStreamFactoryContextImpl,
|
| - BrowserThread::DeleteOnIOThread>
|
| - factory_context_;
|
| -};
|
| -
|
| -TEST_F(RendererAudioOutputStreamFactoryIntegrationTest, StreamIntegrationTest) {
|
| - // Sets up the factory on the IO thread and runs client code on the UI thread.
|
| - // Send a sine wave from the client and makes sure it's received by the output
|
| - // stream.
|
| - MockAudioOutputStream* stream = new MockAudioOutputStream(
|
| - static_cast<MockAudioManager*>(audio_manager_.get()));
|
| -
|
| - // Make sure the mock audio manager uses our mock stream.
|
| - EXPECT_CALL(*static_cast<MockAudioManager*>(audio_manager_.get()),
|
| - MakeLowLatencyOutputStream(_, "", _))
|
| - .WillOnce(Return(stream));
|
| - EXPECT_CALL(*static_cast<MockAudioManager*>(audio_manager_.get()),
|
| - GetPreferredOutputStreamParameters(_, _))
|
| - .WillRepeatedly(Return(GetTestAudioParameters()));
|
| -
|
| - AudioOutputStreamFactoryPtr factory_ptr;
|
| - BrowserThread::PostTask(
|
| - BrowserThread::IO, FROM_HERE,
|
| - base::Bind(&RendererAudioOutputStreamFactoryIntegrationTest::
|
| - CreateAndBindFactory,
|
| - base::Unretained(this),
|
| - base::Passed(mojo::MakeRequest(&factory_ptr))));
|
| -
|
| - AudioOutputStreamProviderPtr provider_ptr;
|
| - base::RunLoop loop;
|
| - media::OutputDeviceStatus status;
|
| - media::AudioParameters params;
|
| - std::string id;
|
| - factory_ptr->RequestDeviceAuthorization(
|
| - mojo::MakeRequest(&provider_ptr), kNoSessionId, "default",
|
| - base::Bind(&AuthCallback, base::Passed(loop.QuitWhenIdleClosure()),
|
| - base::Unretained(&status), base::Unretained(¶ms),
|
| - base::Unretained(&id)));
|
| - loop.Run();
|
| - ASSERT_EQ(status, media::OUTPUT_DEVICE_STATUS_OK);
|
| - ASSERT_EQ(GetTestAudioParameters().AsHumanReadableString(),
|
| - params.AsHumanReadableString());
|
| - ASSERT_TRUE(id.empty());
|
| -
|
| - AudioOutputStreamPtr stream_ptr;
|
| - {
|
| - TestIPCClient client;
|
| - provider_ptr->Acquire(
|
| - mojo::MakeRequest(&stream_ptr), params,
|
| - base::Bind(&TestIPCClient::Start, base::Unretained(&client)));
|
| - SyncWithAllThreads();
|
| - stream_ptr->Play();
|
| - SyncWithAllThreads();
|
| - } // Joining client thread.
|
| - stream_ptr.reset();
|
| - SyncWithAllThreads();
|
| -}
|
| -
|
| -} // namespace content
|
|
|