Commit 70fb1d6b authored by Max Morin's avatar Max Morin Committed by Commit Bot

Add factory for MojoAudioInputStream.

RenderFrameAudioInputStreamFactory is intended to be exposed to the
renderer. The AudioInputStreamHandle class is introduced to bundle a
stream and its client. When moving to the audio service, the
AudioInputStreamHandle will stay in content/ while the
MojoAudioInputStream will be in the audio service.

This diagram shows how we want the stream setup to look in the near
future:
https://docs.google.com/drawings/d/1pSYEqiG46N0OD6rJhfTpohyn2ZkSWsp3ZIHe2PDh0Us/edit

Bug: 653871
Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: I4ff52dbc4797b595e3dd791d1bafa9325a0af9d2
Reviewed-on: https://chromium-review.googlesource.com/771196Reviewed-by: default avatarDmitry Gozman <dgozman@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatarSam McNally <sammc@chromium.org>
Reviewed-by: default avatarOlga Sharonova <olka@chromium.org>
Commit-Queue: Max Morin <maxmorin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#524020}
parent 7a024b0d
......@@ -1317,6 +1317,8 @@ jumbo_source_set("browser") {
"renderer_host/media/audio_input_device_manager.h",
"renderer_host/media/audio_input_renderer_host.cc",
"renderer_host/media/audio_input_renderer_host.h",
"renderer_host/media/audio_input_stream_handle.cc",
"renderer_host/media/audio_input_stream_handle.h",
"renderer_host/media/audio_input_sync_writer.cc",
"renderer_host/media/audio_input_sync_writer.h",
"renderer_host/media/audio_output_authorization_handler.cc",
......@@ -1350,6 +1352,8 @@ jumbo_source_set("browser") {
"renderer_host/media/media_stream_track_metrics_host.h",
"renderer_host/media/media_stream_ui_proxy.cc",
"renderer_host/media/media_stream_ui_proxy.h",
"renderer_host/media/render_frame_audio_input_stream_factory.cc",
"renderer_host/media/render_frame_audio_input_stream_factory.h",
"renderer_host/media/render_frame_audio_output_stream_factory.cc",
"renderer_host/media/render_frame_audio_output_stream_factory.h",
"renderer_host/media/renderer_audio_output_stream_factory_context.h",
......
// 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/audio_input_stream_handle.h"
#include <utility>
#include "base/bind_helpers.h"
#include "mojo/public/cpp/bindings/interface_request.h"
namespace content {
namespace {
media::mojom::AudioInputStreamClientPtr CreatePtrAndStoreRequest(
media::mojom::AudioInputStreamClientRequest* request_out) {
media::mojom::AudioInputStreamClientPtr ptr;
*request_out = mojo::MakeRequest(&ptr);
return ptr;
}
} // namespace
AudioInputStreamHandle::AudioInputStreamHandle(
mojom::RendererAudioInputStreamFactoryClientPtr client,
media::MojoAudioInputStream::CreateDelegateCallback
create_delegate_callback,
DeleterCallback deleter_callback)
: deleter_callback_(std::move(deleter_callback)),
client_(std::move(client)),
stream_ptr_(),
stream_client_request_(),
stream_(mojo::MakeRequest(&stream_ptr_),
CreatePtrAndStoreRequest(&stream_client_request_),
std::move(create_delegate_callback),
base::BindOnce(&AudioInputStreamHandle::OnCreated,
base::Unretained(this)),
base::BindOnce(&AudioInputStreamHandle::CallDeleter,
base::Unretained(this))) {
// Unretained is safe since |this| owns |stream_| and |client_|.
DCHECK(client_);
DCHECK(deleter_callback_);
client_.set_connection_error_handler(base::BindOnce(
&AudioInputStreamHandle::CallDeleter, base::Unretained(this)));
}
AudioInputStreamHandle::~AudioInputStreamHandle() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void AudioInputStreamHandle::OnCreated(
mojo::ScopedSharedBufferHandle shared_buffer,
mojo::ScopedHandle socket_descriptor,
bool initially_muted) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(client_);
DCHECK(deleter_callback_)
<< "|deleter_callback_| was called, but |this| hasn't been destructed!";
client_->StreamCreated(
std::move(stream_ptr_), std::move(stream_client_request_),
std::move(shared_buffer), std::move(socket_descriptor), initially_muted);
}
void AudioInputStreamHandle::CallDeleter() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(deleter_callback_);
std::move(deleter_callback_).Run(this);
}
} // namespace content
// 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.
#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_STREAM_HANDLE_H_
#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_STREAM_HANDLE_H_
#include "base/callback.h"
#include "base/macros.h"
#include "base/sequence_checker.h"
#include "content/common/content_export.h"
#include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
#include "media/mojo/interfaces/audio_input_stream.mojom.h"
#include "media/mojo/services/mojo_audio_input_stream.h"
#include "mojo/public/cpp/system/buffer.h"
#include "mojo/public/cpp/system/handle.h"
namespace content {
// This class creates a MojoAudioInputStream and forwards the OnCreated event
// to a RendererAudioInputStreamFactoryClient.
class CONTENT_EXPORT AudioInputStreamHandle {
public:
using DeleterCallback = base::OnceCallback<void(AudioInputStreamHandle*)>;
// |deleter_callback| will be called when encountering an error, in which
// case |this| should be synchronously destructed by its owner.
AudioInputStreamHandle(mojom::RendererAudioInputStreamFactoryClientPtr client,
media::MojoAudioInputStream::CreateDelegateCallback
create_delegate_callback,
DeleterCallback deleter_callback);
~AudioInputStreamHandle();
private:
void OnCreated(mojo::ScopedSharedBufferHandle shared_buffer,
mojo::ScopedHandle socket_descriptor,
bool initially_muted);
void CallDeleter();
SEQUENCE_CHECKER(sequence_checker_);
DeleterCallback deleter_callback_;
mojom::RendererAudioInputStreamFactoryClientPtr client_;
media::mojom::AudioInputStreamPtr stream_ptr_;
media::mojom::AudioInputStreamClientRequest stream_client_request_;
media::MojoAudioInputStream stream_;
DISALLOW_COPY_AND_ASSIGN(AudioInputStreamHandle);
};
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_AUDIO_INPUT_STREAM_HANDLE_H_
// 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/audio_input_stream_handle.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/memory/shared_memory.h"
#include "base/memory/shared_memory_handle.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/sync_socket.h"
#include "base/test/mock_callback.h"
#include "media/audio/audio_input_delegate.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
using testing::StrictMock;
using testing::Mock;
using testing::Test;
class FakeAudioInputDelegate : public media::AudioInputDelegate {
public:
FakeAudioInputDelegate() {}
~FakeAudioInputDelegate() override {}
int GetStreamId() override { return 0; };
void OnRecordStream() override{};
void OnSetVolume(double volume) override{};
private:
DISALLOW_COPY_AND_ASSIGN(FakeAudioInputDelegate);
};
class MockRendererAudioInputStreamFactoryClient
: public mojom::RendererAudioInputStreamFactoryClient {
public:
MOCK_METHOD0(Created, void());
void StreamCreated(media::mojom::AudioInputStreamPtr input_stream,
media::mojom::AudioInputStreamClientRequest client_request,
mojo::ScopedSharedBufferHandle shared_buffer,
mojo::ScopedHandle socket_descriptor,
bool initially_muted) override {
input_stream_ = std::move(input_stream);
client_request_ = std::move(client_request);
Created();
}
private:
media::mojom::AudioInputStreamPtr input_stream_;
media::mojom::AudioInputStreamClientRequest client_request_;
};
using MockDeleter =
base::MockCallback<base::OnceCallback<void(AudioInputStreamHandle*)>>;
// Creates a fake delegate and saves the provided event handler in
// |event_handler_out|.
std::unique_ptr<media::AudioInputDelegate> CreateFakeDelegate(
media::AudioInputDelegate::EventHandler** event_handler_out,
media::AudioInputDelegate::EventHandler* event_handler) {
*event_handler_out = event_handler;
return std::make_unique<FakeAudioInputDelegate>();
}
} // namespace
class AudioInputStreamHandleTest : public Test {
public:
AudioInputStreamHandleTest()
: client_binding_(&client_, mojo::MakeRequest(&client_ptr_)),
handle_(std::make_unique<AudioInputStreamHandle>(
std::move(client_ptr_),
base::BindOnce(&CreateFakeDelegate, &event_handler_),
deleter_.Get())),
local_(std::make_unique<base::CancelableSyncSocket>()),
remote_(std::make_unique<base::CancelableSyncSocket>()) {
// Wait for |event_handler| to be set.
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(event_handler_);
const size_t kSize = 1234;
base::SharedMemoryCreateOptions shmem_options;
shmem_options.size = kSize;
shmem_options.share_read_only = true;
shared_memory_.Create(shmem_options);
shared_memory_.Map(kSize);
EXPECT_TRUE(
base::CancelableSyncSocket::CreatePair(local_.get(), remote_.get()));
}
void SendCreatedNotification() {
const int kIrrelevantStreamId = 0;
const bool kInitiallyMuted = false;
event_handler_->OnStreamCreated(kIrrelevantStreamId, &shared_memory_,
std::move(remote_), kInitiallyMuted);
}
MockRendererAudioInputStreamFactoryClient* client() { return &client_; }
void UnbindClientBinding() { client_binding_.Unbind(); }
void ExpectHandleWillCallDeleter() {
EXPECT_CALL(deleter_, Run(handle_.release()))
.WillOnce(testing::DeleteArg<0>());
}
// Note: Must call ExpectHandleWillCallDeleter() first.
void VerifyDeleterWasCalled() {
EXPECT_TRUE(Mock::VerifyAndClear(&deleter_));
}
private:
base::MessageLoop message_loop_;
StrictMock<MockRendererAudioInputStreamFactoryClient> client_;
mojom::RendererAudioInputStreamFactoryClientPtr client_ptr_;
mojo::Binding<mojom::RendererAudioInputStreamFactoryClient> client_binding_;
StrictMock<MockDeleter> deleter_;
media::AudioInputDelegate::EventHandler* event_handler_ = nullptr;
std::unique_ptr<AudioInputStreamHandle> handle_;
base::SharedMemory shared_memory_;
std::unique_ptr<base::CancelableSyncSocket> local_;
std::unique_ptr<base::CancelableSyncSocket> remote_;
};
TEST_F(AudioInputStreamHandleTest, CreateStream) {
EXPECT_CALL(*client(), Created());
SendCreatedNotification();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(Mock::VerifyAndClear(client()));
}
TEST_F(AudioInputStreamHandleTest,
DestructClientBeforeCreationFinishes_CancelsStreamCreation) {
ExpectHandleWillCallDeleter();
UnbindClientBinding();
base::RunLoop().RunUntilIdle();
VerifyDeleterWasCalled();
}
TEST_F(AudioInputStreamHandleTest,
CreateStreamAndDisconnectClient_DestroysStream) {
EXPECT_CALL(*client(), Created());
SendCreatedNotification();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(Mock::VerifyAndClear(client()));
ExpectHandleWillCallDeleter();
UnbindClientBinding();
base::RunLoop().RunUntilIdle();
VerifyDeleterWasCalled();
}
} // namespace content
// 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/render_frame_audio_input_stream_factory.h"
#include <utility>
#include "base/task_runner_util.h"
#include "content/browser/media/media_internals.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "media/base/audio_parameters.h"
namespace content {
// static
std::unique_ptr<RenderFrameAudioInputStreamFactoryHandle,
BrowserThread::DeleteOnIOThread>
RenderFrameAudioInputStreamFactoryHandle::CreateFactory(
RenderFrameAudioInputStreamFactory::CreateDelegateCallback
create_delegate_callback,
content::MediaStreamManager* media_stream_manager,
mojom::RendererAudioInputStreamFactoryRequest request) {
std::unique_ptr<RenderFrameAudioInputStreamFactoryHandle,
BrowserThread::DeleteOnIOThread>
handle(new RenderFrameAudioInputStreamFactoryHandle(
std::move(create_delegate_callback), media_stream_manager));
// Unretained is safe since |*handle| must be posted to the IO thread prior to
// deletion.
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::BindOnce(&RenderFrameAudioInputStreamFactoryHandle::Init,
base::Unretained(handle.get()), std::move(request)));
return handle;
}
RenderFrameAudioInputStreamFactoryHandle::
~RenderFrameAudioInputStreamFactoryHandle() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
RenderFrameAudioInputStreamFactoryHandle::
RenderFrameAudioInputStreamFactoryHandle(
RenderFrameAudioInputStreamFactory::CreateDelegateCallback
create_delegate_callback,
MediaStreamManager* media_stream_manager)
: impl_(std::move(create_delegate_callback), media_stream_manager),
binding_(&impl_) {}
void RenderFrameAudioInputStreamFactoryHandle::Init(
mojom::RendererAudioInputStreamFactoryRequest request) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
binding_.Bind(std::move(request));
}
RenderFrameAudioInputStreamFactory::RenderFrameAudioInputStreamFactory(
CreateDelegateCallback create_delegate_callback,
MediaStreamManager* media_stream_manager)
: create_delegate_callback_(std::move(create_delegate_callback)),
media_stream_manager_(media_stream_manager),
audio_log_(MediaInternals::GetInstance()->CreateAudioLog(
media::AudioLogFactory::AUDIO_INPUT_CONTROLLER)),
weak_ptr_factory_(this) {
DCHECK(create_delegate_callback_);
// No thread-hostile state has been initialized yet, so we don't have to bind
// to this specific thread.
}
RenderFrameAudioInputStreamFactory::~RenderFrameAudioInputStreamFactory() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
}
void RenderFrameAudioInputStreamFactory::CreateStream(
mojom::RendererAudioInputStreamFactoryClientPtr client,
int32_t session_id,
const media::AudioParameters& audio_params,
bool automatic_gain_control,
uint32_t shared_memory_count) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
#if defined(OS_CHROMEOS)
if (audio_params.channel_layout() ==
media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) {
media_stream_manager_->audio_input_device_manager()
->RegisterKeyboardMicStream(base::BindOnce(
&RenderFrameAudioInputStreamFactory::DoCreateStream,
weak_ptr_factory_.GetWeakPtr(), std::move(client), session_id,
audio_params, automatic_gain_control, shared_memory_count));
return;
}
#endif
DoCreateStream(std::move(client), session_id, audio_params,
automatic_gain_control, shared_memory_count,
AudioInputDeviceManager::KeyboardMicRegistration());
}
void RenderFrameAudioInputStreamFactory::DoCreateStream(
mojom::RendererAudioInputStreamFactoryClientPtr client,
int session_id,
const media::AudioParameters& audio_params,
bool automatic_gain_control,
uint32_t shared_memory_count,
AudioInputDeviceManager::KeyboardMicRegistration
keyboard_mic_registration) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
int stream_id = ++next_stream_id_;
// Unretained is safe since |this| owns |streams_|.
streams_.insert(std::make_unique<AudioInputStreamHandle>(
std::move(client),
base::BindOnce(
create_delegate_callback_,
base::Unretained(media_stream_manager_->audio_input_device_manager()),
audio_log_.get(), std::move(keyboard_mic_registration),
shared_memory_count, stream_id, session_id, automatic_gain_control,
audio_params),
base::BindOnce(&RenderFrameAudioInputStreamFactory::RemoveStream,
weak_ptr_factory_.GetWeakPtr())));
}
void RenderFrameAudioInputStreamFactory::RemoveStream(
AudioInputStreamHandle* stream) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
streams_.erase(stream);
}
} // namespace content
// 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.
#ifndef CONTENT_BROWSER_RENDERER_HOST_MEDIA_RENDER_FRAME_AUDIO_INPUT_STREAM_FACTORY_H_
#define CONTENT_BROWSER_RENDERER_HOST_MEDIA_RENDER_FRAME_AUDIO_INPUT_STREAM_FACTORY_H_
#include <cstdint>
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/containers/flat_set.h"
#include "base/containers/unique_ptr_comparator.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "build/build_config.h"
#include "content/browser/renderer_host/media/audio_input_device_manager.h"
#include "content/browser/renderer_host/media/audio_input_stream_handle.h"
#include "content/common/content_export.h"
#include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
#include "content/public/browser/browser_thread.h"
#include "media/audio/audio_input_delegate.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace media {
class AudioParameters;
class AudioLog;
} // namespace media
namespace content {
class MediaStreamManager;
// Handles a RendererAudioInputStreamFactory request for a render frame host,
// using the provided RendererAudioInputStreamFactoryContext. This class may
// be constructed on any thread, but must be used on the IO thread after that,
// and also destructed on the IO thread.
class CONTENT_EXPORT RenderFrameAudioInputStreamFactory
: public mojom::RendererAudioInputStreamFactory {
public:
using CreateDelegateCallback =
base::RepeatingCallback<std::unique_ptr<media::AudioInputDelegate>(
AudioInputDeviceManager* audio_input_device_manager,
media::AudioLog* audio_log,
AudioInputDeviceManager::KeyboardMicRegistration
keyboard_mic_registration,
uint32_t shared_memory_count,
int stream_id,
int session_id,
bool automatic_gain_control,
const media::AudioParameters& parameters,
media::AudioInputDelegate::EventHandler* event_handler)>;
RenderFrameAudioInputStreamFactory(
CreateDelegateCallback create_delegate_callback,
MediaStreamManager* media_stream_manager);
~RenderFrameAudioInputStreamFactory() override;
private:
using InputStreamSet = base::flat_set<std::unique_ptr<AudioInputStreamHandle>,
base::UniquePtrComparator>;
// mojom::RendererAudioInputStreamFactory implementation.
void CreateStream(mojom::RendererAudioInputStreamFactoryClientPtr client,
int32_t session_id,
const media::AudioParameters& audio_params,
bool automatic_gain_control,
uint32_t shared_memory_count) override;
void DoCreateStream(mojom::RendererAudioInputStreamFactoryClientPtr client,
int session_id,
const media::AudioParameters& audio_params,
bool automatic_gain_control,
uint32_t shared_memory_count,
AudioInputDeviceManager::KeyboardMicRegistration
keyboard_mic_registration);
void RemoveStream(AudioInputStreamHandle* input_stream);
const CreateDelegateCallback create_delegate_callback_;
MediaStreamManager* media_stream_manager_;
const std::unique_ptr<media::AudioLog> audio_log_;
InputStreamSet streams_;
int next_stream_id_ = 0;
base::WeakPtrFactory<RenderFrameAudioInputStreamFactory> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(RenderFrameAudioInputStreamFactory);
};
// This class is a convenient bundle of factory and binding.
// It can be created on any thread, but should be destroyed on the IO thread
// (hence the DeleteOnIOThread pointer).
class CONTENT_EXPORT RenderFrameAudioInputStreamFactoryHandle {
public:
static std::unique_ptr<RenderFrameAudioInputStreamFactoryHandle,
BrowserThread::DeleteOnIOThread>
CreateFactory(RenderFrameAudioInputStreamFactory::CreateDelegateCallback
create_delegate_callback,
MediaStreamManager* media_stream_manager,
mojom::RendererAudioInputStreamFactoryRequest request);
~RenderFrameAudioInputStreamFactoryHandle();
private:
RenderFrameAudioInputStreamFactoryHandle(
RenderFrameAudioInputStreamFactory::CreateDelegateCallback
create_delegate_callback,
MediaStreamManager* media_stream_manager);
void Init(mojom::RendererAudioInputStreamFactoryRequest request);
RenderFrameAudioInputStreamFactory impl_;
mojo::Binding<mojom::RendererAudioInputStreamFactory> binding_;
DISALLOW_COPY_AND_ASSIGN(RenderFrameAudioInputStreamFactoryHandle);
};
using UniqueAudioInputStreamFactoryPtr =
std::unique_ptr<RenderFrameAudioInputStreamFactoryHandle,
BrowserThread::DeleteOnIOThread>;
} // namespace content
#endif // CONTENT_BROWSER_RENDERER_HOST_MEDIA_RENDER_FRAME_AUDIO_INPUT_STREAM_FACTORY_H_
// 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/render_frame_audio_input_stream_factory.h"
#include <limits>
#include <utility>
#include "base/bind.h"
#include "base/memory/shared_memory.h"
#include "base/memory/shared_memory_handle.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/sync_socket.h"
#include "content/browser/renderer_host/media/audio_input_device_manager.h"
#include "content/browser/renderer_host/media/media_stream_manager.h"
#include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "media/audio/audio_system_impl.h"
#include "media/audio/mock_audio_manager.h"
#include "media/audio/test_audio_thread.h"
#include "media/base/audio_parameters.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::Test;
const size_t kShmemSize = 1234;
const int kSessionId = 234;
const bool kAGC = false;
const uint32_t kSharedMemoryCount = 345;
const int kSampleFrequency = 44100;
const int kBitsPerSample = 16;
const int kSamplesPerBuffer = kSampleFrequency / 100;
const bool kInitiallyMuted = false;
media::AudioParameters GetTestAudioParameters() {
return media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::CHANNEL_LAYOUT_MONO, kSampleFrequency,
kBitsPerSample, kSamplesPerBuffer);
}
class FakeAudioInputDelegate : public media::AudioInputDelegate {
public:
FakeAudioInputDelegate() {}
~FakeAudioInputDelegate() override {}
int GetStreamId() override { return 0; };
void OnRecordStream() override{};
void OnSetVolume(double volume) override{};
private:
DISALLOW_COPY_AND_ASSIGN(FakeAudioInputDelegate);
};
class FakeAudioInputStreamClient : public media::mojom::AudioInputStreamClient {
public:
void OnMutedStateChanged(bool is_muted) override {}
void OnError() override {}
};
class MockRendererAudioInputStreamFactoryClient
: public mojom::RendererAudioInputStreamFactoryClient {
public:
MOCK_METHOD0(Created, void());
void StreamCreated(media::mojom::AudioInputStreamPtr input_stream,
media::mojom::AudioInputStreamClientRequest client_request,
mojo::ScopedSharedBufferHandle shared_buffer,
mojo::ScopedHandle socket_descriptor,
bool initially_muted) override {
Created();
}
};
// Creates a fake delegate and saves the provided event handler in
// |event_handler_out|.
std::unique_ptr<media::AudioInputDelegate> CreateFakeDelegate(
media::AudioInputDelegate::EventHandler** event_handler_out,
AudioInputDeviceManager* audio_input_device_manager,
media::AudioLog* audio_log,
AudioInputDeviceManager::KeyboardMicRegistration keyboard_mic_registration,
uint32_t shared_memory_count,
int stream_id,
int session_id,
bool automatic_gain_control,
const media::AudioParameters& parameters,
media::AudioInputDelegate::EventHandler* event_handler) {
*event_handler_out = event_handler;
return std::make_unique<FakeAudioInputDelegate>();
}
} // namespace
class RenderFrameAudioInputStreamFactoryTest : public testing::Test {
public:
RenderFrameAudioInputStreamFactoryTest()
: thread_bundle_(base::in_place),
audio_manager_(std::make_unique<media::TestAudioThread>()),
audio_system_(&audio_manager_),
media_stream_manager_(&audio_system_, audio_manager_.GetTaskRunner()),
client_binding_(&client_, mojo::MakeRequest(&client_ptr_)),
factory_handle_(RenderFrameAudioInputStreamFactoryHandle::CreateFactory(
base::BindRepeating(&CreateFakeDelegate, &event_handler_),
&media_stream_manager_,
mojo::MakeRequest(&factory_ptr_))) {}
~RenderFrameAudioInputStreamFactoryTest() override {
audio_manager_.Shutdown();
thread_bundle_.reset();
}
base::Optional<TestBrowserThreadBundle> thread_bundle_;
media::MockAudioManager audio_manager_;
media::AudioSystemImpl audio_system_;
MediaStreamManager media_stream_manager_;
mojom::RendererAudioInputStreamFactoryPtr factory_ptr_;
media::mojom::AudioInputStreamPtr stream_ptr_;
MockRendererAudioInputStreamFactoryClient client_;
mojom::RendererAudioInputStreamFactoryClientPtr client_ptr_;
media::AudioInputDelegate::EventHandler* event_handler_ = nullptr;
mojo::Binding<mojom::RendererAudioInputStreamFactoryClient> client_binding_;
UniqueAudioInputStreamFactoryPtr factory_handle_;
};
TEST_F(RenderFrameAudioInputStreamFactoryTest, CreateStream) {
factory_ptr_->CreateStream(std::move(client_ptr_), kSessionId,
GetTestAudioParameters(), kAGC,
kSharedMemoryCount);
// Wait for delegate to be created and |event_handler| set.
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(event_handler_);
base::SharedMemoryCreateOptions shmem_options;
shmem_options.size = kShmemSize;
shmem_options.share_read_only = true;
base::SharedMemory shared_memory;
shared_memory.Create(shmem_options);
shared_memory.Map(kShmemSize);
auto local = std::make_unique<base::CancelableSyncSocket>();
auto remote = std::make_unique<base::CancelableSyncSocket>();
ASSERT_TRUE(
base::CancelableSyncSocket::CreatePair(local.get(), remote.get()));
event_handler_->OnStreamCreated(/*stream_id, irrelevant*/ 0, &shared_memory,
std::move(remote), kInitiallyMuted);
EXPECT_CALL(client_, Created());
base::RunLoop().RunUntilIdle();
}
} // namespace content
......@@ -75,22 +75,12 @@ RenderFrameAudioOutputStreamFactory::~RenderFrameAudioOutputStreamFactory() {
void RenderFrameAudioOutputStreamFactory::RequestDeviceAuthorization(
media::mojom::AudioOutputStreamProviderRequest stream_provider_request,
int64_t session_id,
int32_t session_id,
const std::string& device_id,
RequestDeviceAuthorizationCallback callback) {
DCHECK(thread_checker_.CalledOnValidThread());
const base::TimeTicks auth_start_time = base::TimeTicks::Now();
if (!base::IsValueInRangeForNumericType<int>(session_id)) {
mojo::ReportBadMessage("session_id is not in integer range");
// Note: We must call the callback even though we are killing the renderer.
// This is mandated by mojo.
std::move(callback).Run(
media::OutputDeviceStatus::OUTPUT_DEVICE_STATUS_ERROR_NOT_AUTHORIZED,
media::AudioParameters::UnavailableDeviceParams(), std::string());
return;
}
context_->RequestDeviceAuthorization(
render_frame_id_, session_id, device_id,
base::BindOnce(
......@@ -143,11 +133,7 @@ void RenderFrameAudioOutputStreamFactory::RemoveStream(
media::mojom::AudioOutputStreamProvider* stream_provider) {
DCHECK(thread_checker_.CalledOnValidThread());
base::EraseIf(
stream_providers_,
[stream_provider](
const std::unique_ptr<media::mojom::AudioOutputStreamProvider>&
other) { return other.get() == stream_provider; });
stream_providers_.erase(stream_provider);
}
} // namespace content
......@@ -9,6 +9,7 @@
#include <string>
#include "base/containers/flat_set.h"
#include "base/containers/unique_ptr_comparator.h"
#include "base/threading/thread_checker.h"
#include "content/common/content_export.h"
#include "content/common/media/renderer_audio_output_stream_factory.mojom.h"
......@@ -33,12 +34,13 @@ class CONTENT_EXPORT RenderFrameAudioOutputStreamFactory
private:
using OutputStreamProviderSet =
base::flat_set<std::unique_ptr<media::mojom::AudioOutputStreamProvider>>;
base::flat_set<std::unique_ptr<media::mojom::AudioOutputStreamProvider>,
base::UniquePtrComparator>;
// mojom::RendererAudioOutputStreamFactory implementation.
void RequestDeviceAuthorization(
media::mojom::AudioOutputStreamProviderRequest stream_provider,
int64_t session_id,
int32_t session_id,
const std::string& device_id,
RequestDeviceAuthorizationCallback callback) override;
......
......@@ -19,7 +19,6 @@
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "media/base/audio_parameters.h"
#include "mojo/edk/embedder/embedder.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -343,38 +342,4 @@ TEST(RenderFrameAudioOutputStreamFactoryTest, DelegateError_DeletesStream) {
EXPECT_TRUE(delegate_is_destructed);
}
TEST(RenderFrameAudioOutputStreamFactoryTest, OutOfRangeSessionId_BadMessage) {
// This test checks that we get a bad message if session_id is too large
// to fit in an integer. This ensures that we don't overflow when casting the
// int64_t to an int
if (sizeof(int) >= sizeof(int64_t)) {
// In this case, any int64_t would fit in an int, and the case we are
// checking for is impossible.
return;
}
bool got_bad_message = false;
mojo::edk::SetDefaultProcessErrorCallback(
base::Bind([](bool* got_bad_message,
const std::string& s) { *got_bad_message = true; },
&got_bad_message));
TestBrowserThreadBundle thread_bundle;
AudioOutputStreamProviderPtr output_provider;
auto factory_context = std::make_unique<MockContext>(true);
auto factory_ptr = factory_context->CreateFactory();
int64_t session_id = std::numeric_limits<int>::max();
++session_id;
EXPECT_FALSE(got_bad_message);
factory_ptr->RequestDeviceAuthorization(
mojo::MakeRequest(&output_provider), session_id, "default",
base::BindOnce([](media::OutputDeviceStatus,
const media::AudioParameters&, const std::string&) {}));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(got_bad_message);
}
} // namespace content
......@@ -557,6 +557,7 @@ mojom("mojo_bindings") {
"leveldb_wrapper.mojom",
"manifest_observer.mojom",
"media/media_stream.mojom",
"media/renderer_audio_input_stream_factory.mojom",
"media/renderer_audio_output_stream_factory.mojom",
"memory_coordinator.mojom",
"native_types.mojom",
......
// 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.
module content.mojom;
import "media/mojo/interfaces/audio_input_stream.mojom";
import "media/mojo/interfaces/audio_parameters.mojom";
import "media/mojo/interfaces/media_types.mojom";
// This interface is used by the renderer to ask the browser to create input
// streams. The renderer supplies the desired audio parameters, and a client
// to send the stream to when it's ready. The lifetime of the stream is limited
// by the lifetime of the client.
interface RendererAudioInputStreamFactory {
CreateStream(
RendererAudioInputStreamFactoryClient client,
int32 session_id,
media.mojom.AudioParameters params,
bool automatic_gain_control,
uint32 shared_memory_count);
};
interface RendererAudioInputStreamFactoryClient {
// Called when a stream has been created. Will only be called once for every
// CreateStream call.
// TODO(crbug.com/787806): There are plans to allow this function to be called
// serveral times in the future. If the stream is terminated e.g. due to the
// process hosting it crashing, this function should be called again with a
// fresh stream.
StreamCreated(
media.mojom.AudioInputStream stream,
media.mojom.AudioInputStreamClient& client_request,
handle<shared_buffer> shared_buffer, handle socket_descriptor,
bool initially_muted);
};
......@@ -17,7 +17,7 @@ interface RendererAudioOutputStreamFactory {
// (in case of an error).
RequestDeviceAuthorization(
media.mojom.AudioOutputStreamProvider& stream_provider_request,
int64 session_id,
int32 session_id,
string device_id) =>
(media.mojom.OutputDeviceStatus state,
media.mojom.AudioParameters output_params,
......
......@@ -41,7 +41,7 @@ class FakeRemoteFactory : public mojom::RendererAudioOutputStreamFactory {
void RequestDeviceAuthorization(
media::mojom::AudioOutputStreamProviderRequest stream_provider,
int64_t session_id,
int32_t session_id,
const std::string& device_id,
RequestDeviceAuthorizationCallback callback) override {
std::move(callback).Run(
......
......@@ -176,6 +176,8 @@ bool MojoAudioOutputIPC::DoRequestDeviceAuthorization(
return false;
}
static_assert(sizeof(int) == sizeof(int32_t),
"sizeof(int) == sizeof(int32_t)");
factory->RequestDeviceAuthorization(MakeProviderRequest(), session_id,
device_id, std::move(callback));
return true;
......
......@@ -98,7 +98,7 @@ class TestRemoteFactory : public mojom::RendererAudioOutputStreamFactory {
void RequestDeviceAuthorization(
media::mojom::AudioOutputStreamProviderRequest stream_provider_request,
int64_t session_id,
int32_t session_id,
const std::string& device_id,
RequestDeviceAuthorizationCallback callback) override {
EXPECT_EQ(session_id, expected_session_id_);
......@@ -119,7 +119,7 @@ class TestRemoteFactory : public mojom::RendererAudioOutputStreamFactory {
}
void PrepareProviderForAuthorization(
int64_t session_id,
int32_t session_id,
const std::string& device_id,
std::unique_ptr<TestStreamProvider> provider) {
EXPECT_FALSE(expect_request_);
......@@ -130,7 +130,7 @@ class TestRemoteFactory : public mojom::RendererAudioOutputStreamFactory {
std::swap(provider_, provider);
}
void RefuseNextRequest(int64_t session_id, const std::string& device_id) {
void RefuseNextRequest(int32_t session_id, const std::string& device_id) {
EXPECT_FALSE(expect_request_);
expect_request_ = true;
expected_session_id_ = session_id;
......@@ -158,7 +158,7 @@ class TestRemoteFactory : public mojom::RendererAudioOutputStreamFactory {
mojom::RendererAudioOutputStreamFactory* get() { return this_proxy_.get(); }
bool expect_request_;
int64_t expected_session_id_;
int32_t expected_session_id_;
std::string expected_device_id_;
mojom::RendererAudioOutputStreamFactoryPtr this_proxy_;
......
......@@ -1368,6 +1368,7 @@ test("content_unittests") {
"../browser/renderer_host/media/audio_input_delegate_impl_unittest.cc",
"../browser/renderer_host/media/audio_input_device_manager_unittest.cc",
"../browser/renderer_host/media/audio_input_renderer_host_unittest.cc",
"../browser/renderer_host/media/audio_input_stream_handle_unittest.cc",
"../browser/renderer_host/media/audio_input_sync_writer_unittest.cc",
"../browser/renderer_host/media/audio_output_authorization_handler_unittest.cc",
"../browser/renderer_host/media/audio_output_delegate_impl_unittest.cc",
......@@ -1384,6 +1385,7 @@ test("content_unittests") {
"../browser/renderer_host/media/media_stream_ui_proxy_unittest.cc",
"../browser/renderer_host/media/mock_video_capture_provider.cc",
"../browser/renderer_host/media/mock_video_capture_provider.h",
"../browser/renderer_host/media/render_frame_audio_input_stream_factory_unittest.cc",
"../browser/renderer_host/media/render_frame_audio_output_stream_factory_unittest.cc",
"../browser/renderer_host/media/service_video_capture_device_launcher_unittest.cc",
"../browser/renderer_host/media/service_video_capture_provider_unittest.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment