Commit 4a0c43b1 authored by Marina Ciocea's avatar Marina Ciocea Committed by Commit Bot

Add InputStream to audio service.

Add implementation of media::mojom::AudioInputStream to audio service.
This implementation is not used yet, it will be used in a future CL
introducing input stream factory. UserInputMonitor is also not used,
and will be moved to audio service in a future CL.

Also removed AIC::Create branch that returns null if params are invalid,
as params are verified many times along the way to AIC and should be valid.

Bug: 803102
Cq-Include-Trybots: luci.chromium.try:android_optional_gpu_tests_rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: Ib0b3c381bb9d5493e22047f13d093a87c252229c
Reviewed-on: https://chromium-review.googlesource.com/975642
Commit-Queue: Marina Ciocea <marinaciocea@chromium.org>
Reviewed-by: default avatarOlga Sharonova <olka@chromium.org>
Reviewed-by: default avatarMax Morin <maxmorin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#548735}
parent 39219b44
......@@ -209,6 +209,8 @@ scoped_refptr<AudioInputController> AudioInputController::Create(
DCHECK(sync_writer);
DCHECK(event_handler);
// TODO(https://crbug.com/803102): remove check after switching to input
// stream factory.
if (!params.IsValid() || (params.channels() > kMaxInputChannels))
return nullptr;
......@@ -231,13 +233,10 @@ scoped_refptr<AudioInputController> AudioInputController::Create(
// Create and open a new audio input stream from the existing
// audio-device thread. Use the provided audio-input device.
if (!controller->task_runner_->PostTask(
FROM_HERE, base::BindOnce(&AudioInputController::DoCreate, controller,
base::Unretained(audio_manager), params,
device_id, enable_agc))) {
controller = nullptr;
}
controller->task_runner_->PostTask(
FROM_HERE, base::BindOnce(&AudioInputController::DoCreate, controller,
base::Unretained(audio_manager), params,
device_id, enable_agc));
return controller;
}
......@@ -271,13 +270,9 @@ scoped_refptr<AudioInputController> AudioInputController::CreateForStream(
return controller;
}
if (!controller->task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AudioInputController::DoCreateForStream, controller,
stream, false /*enable_agc*/))) {
controller = nullptr;
}
controller->task_runner_->PostTask(
FROM_HERE, base::BindOnce(&AudioInputController::DoCreateForStream,
controller, stream, false /*enable_agc*/));
return controller;
}
......
......@@ -37,6 +37,8 @@ source_set("lib") {
"group_member.h",
"in_process_audio_manager_accessor.cc",
"in_process_audio_manager_accessor.h",
"input_stream.cc",
"input_stream.h",
"output_controller.cc",
"output_controller.h",
"output_stream.cc",
......@@ -69,6 +71,7 @@ source_set("tests") {
sources = [
"debug_recording_unittest.cc",
"group_coordinator_unittest.cc",
"input_stream_unittest.cc",
"output_controller_unittest.cc",
"output_stream_unittest.cc",
"sync_reader_unittest.cc",
......
// Copyright 2018 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 "services/audio/input_stream.h"
#include <string>
#include <utility>
#include "media/audio/audio_input_sync_writer.h"
#include "media/audio/audio_manager.h"
#include "media/base/audio_parameters.h"
#include "media/base/user_input_monitor.h"
#include "mojo/public/cpp/system/buffer.h"
#include "mojo/public/cpp/system/handle.h"
#include "mojo/public/cpp/system/platform_handle.h"
namespace audio {
namespace {
const int kMaxInputChannels = 3;
}
InputStream::InputStream(CreatedCallback created_callback,
DeleteCallback delete_callback,
media::mojom::AudioInputStreamRequest request,
media::mojom::AudioInputStreamClientPtr client,
media::mojom::AudioLogPtr log,
media::AudioManager* audio_manager,
media::UserInputMonitor* user_input_monitor,
const media::AudioParameters& params,
const std::string& device_id,
uint32_t shared_memory_count,
bool enable_agc)
: binding_(this, std::move(request)),
client_(std::move(client)),
log_(media::mojom::ThreadSafeAudioLogPtr::Create(std::move(log))),
created_callback_(std::move(created_callback)),
delete_callback_(std::move(delete_callback)),
foreign_socket_(),
writer_(media::AudioInputSyncWriter::Create(
base::BindRepeating(&media::mojom::AudioLog::OnLogMessage,
base::Unretained(log_->get())),
shared_memory_count,
params,
&foreign_socket_)),
weak_factory_(this) {
DCHECK(audio_manager);
DCHECK(binding_.is_bound());
DCHECK(client_.is_bound());
DCHECK(created_callback_);
DCHECK(delete_callback_);
// |this| owns these objects, so unretained is safe.
base::RepeatingClosure error_handler =
base::BindRepeating(&InputStream::OnStreamError, base::Unretained(this));
binding_.set_connection_error_handler(error_handler);
client_.set_connection_error_handler(error_handler);
log_->get()->OnCreated(params, device_id);
// Only MONO, STEREO and STEREO_AND_KEYBOARD_MIC channel layouts are expected,
// see AudioManagerBase::MakeAudioInputStream().
if (!params.IsValid() || (params.channels() > kMaxInputChannels)) {
OnStreamError();
return;
}
if (!writer_) {
OnStreamError();
return;
}
controller_ = media::AudioInputController::Create(
audio_manager, this, writer_.get(), user_input_monitor, params, device_id,
enable_agc);
}
InputStream::~InputStream() {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
log_->get()->OnClosed();
if (created_callback_) {
// Didn't manage to create the stream. Call the callback anyways as mandated
// by mojo.
std::move(created_callback_).Run(nullptr, false);
}
if (!controller_) {
// Didn't initialize properly, nothing to clean up.
return;
}
// TODO(https://crbug.com/803102): remove AudioInputController::Close() after
// content/ streams are removed, destructor should suffice.
controller_->Close(base::OnceClosure());
}
void InputStream::Record() {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
DCHECK(controller_);
controller_->Record();
log_->get()->OnStarted();
}
void InputStream::SetVolume(double volume) {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
DCHECK(controller_);
if (volume < 0 || volume > 1) {
mojo::ReportBadMessage("Invalid volume");
OnStreamError();
return;
}
controller_->SetVolume(volume);
log_->get()->OnSetVolume(volume);
}
void InputStream::OnCreated(bool initially_muted) {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
const base::SharedMemory* memory = writer_->shared_memory();
base::SharedMemoryHandle foreign_memory_handle = memory->GetReadOnlyHandle();
if (!base::SharedMemory::IsHandleValid(foreign_memory_handle)) {
OnStreamError();
return;
}
mojo::ScopedSharedBufferHandle buffer_handle = mojo::WrapSharedMemoryHandle(
foreign_memory_handle, memory->requested_size(),
mojo::UnwrappedSharedMemoryHandleProtection::kReadOnly);
mojo::ScopedHandle socket_handle =
mojo::WrapPlatformFile(foreign_socket_.Release());
DCHECK(buffer_handle.is_valid());
DCHECK(socket_handle.is_valid());
std::move(created_callback_)
.Run({base::in_place, std::move(buffer_handle), std::move(socket_handle)},
initially_muted);
}
void InputStream::OnError(media::AudioInputController::ErrorCode error_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
client_->OnError();
log_->get()->OnError();
OnStreamError();
}
void InputStream::OnLog(base::StringPiece message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
log_->get()->OnLogMessage(message.as_string());
}
void InputStream::OnMuted(bool is_muted) {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
client_->OnMutedStateChanged(is_muted);
}
void InputStream::OnStreamError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
// Defer callback so we're not destructed while in the constructor.
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(&InputStream::CallDeleter, weak_factory_.GetWeakPtr()));
binding_.Close();
}
void InputStream::CallDeleter() {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
std::move(delete_callback_).Run(this);
}
} // namespace audio
// Copyright 2018 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 SERVICES_AUDIO_INPUT_STREAM_H_
#define SERVICES_AUDIO_INPUT_STREAM_H_
#include <memory>
#include <string>
#include "base/memory/scoped_refptr.h"
#include "base/sync_socket.h"
#include "media/audio/audio_input_controller.h"
#include "media/mojo/interfaces/audio_data_pipe.mojom.h"
#include "media/mojo/interfaces/audio_input_stream.mojom.h"
#include "media/mojo/interfaces/audio_logging.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace media {
class AudioInputSyncWriter;
class AudioManager;
class AudioParameters;
class UserInputMonitor;
} // namespace media
namespace audio {
class InputStream final : public media::mojom::AudioInputStream,
public media::AudioInputController::EventHandler {
public:
using CreatedCallback =
base::OnceCallback<void(media::mojom::AudioDataPipePtr, bool)>;
using DeleteCallback = base::OnceCallback<void(InputStream*)>;
InputStream(CreatedCallback created_callback,
DeleteCallback delete_callback,
media::mojom::AudioInputStreamRequest request,
media::mojom::AudioInputStreamClientPtr client,
media::mojom::AudioLogPtr log,
media::AudioManager* manager,
media::UserInputMonitor* user_input_monitor,
const media::AudioParameters& params,
const std::string& device_id,
uint32_t shared_memory_count,
bool enable_agc);
~InputStream() override;
// media::mojom::AudioInputStream implementation.
void Record() override;
void SetVolume(double volume) override;
// media::AudioInputController::EventHandler implementation.
void OnCreated(bool initially_muted) override;
void OnError(media::AudioInputController::ErrorCode error_code) override;
void OnLog(base::StringPiece) override;
void OnMuted(bool is_muted) override;
private:
void OnStreamError();
void CallDeleter();
mojo::Binding<media::mojom::AudioInputStream> binding_;
media::mojom::AudioInputStreamClientPtr client_;
const scoped_refptr<media::mojom::ThreadSafeAudioLogPtr> log_;
// Notify stream client on creation.
CreatedCallback created_callback_;
// Notify stream factory (audio service) on destruction.
DeleteCallback delete_callback_;
base::CancelableSyncSocket foreign_socket_;
const std::unique_ptr<media::AudioInputSyncWriter> writer_;
scoped_refptr<media::AudioInputController> controller_;
SEQUENCE_CHECKER(owning_sequence_);
base::WeakPtrFactory<InputStream> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(InputStream);
};
} // namespace audio
#endif // SERVICES_AUDIO_INPUT_STREAM_H_
This diff is collapsed.
......@@ -81,7 +81,7 @@ class OutputStream final : public media::mojom::AudioOutputStream,
mojo::Binding<AudioOutputStream> binding_;
media::mojom::AudioOutputStreamClientPtr client_;
media::mojom::AudioOutputStreamObserverAssociatedPtr observer_;
scoped_refptr<media::mojom::ThreadSafeAudioLogPtr> log_;
const scoped_refptr<media::mojom::ThreadSafeAudioLogPtr> log_;
SyncReader reader_;
OutputController controller_;
......
......@@ -31,6 +31,12 @@ using testing::_;
namespace audio {
namespace {
// Aliases for use with MockCreatedCallback::Created().
const bool successfully_ = true;
const bool unsuccessfully_ = false;
class MockStream : public media::AudioOutputStream {
public:
MockStream() {}
......@@ -98,13 +104,6 @@ class MockObserver : public media::mojom::AudioOutputStreamObserver {
DISALLOW_COPY_AND_ASSIGN(MockObserver);
};
// Aliases for use with MockCreatedCallback::Created().
namespace {
const bool successfully_ = true;
const bool unsuccessfully_ = false;
} // namespace
class MockCreatedCallback {
public:
MockCreatedCallback() {}
......@@ -122,6 +121,8 @@ class MockCreatedCallback {
DISALLOW_COPY_AND_ASSIGN(MockCreatedCallback);
};
} // namespace
// Instantiates various classes that we're going to want in most test cases.
class TestEnvironment {
public:
......@@ -133,7 +134,11 @@ class TestEnvironment {
mojo::edk::SetDefaultProcessErrorCallback(bad_message_callback_.Get());
}
~TestEnvironment() { audio_manager_.Shutdown(); }
~TestEnvironment() {
audio_manager_.Shutdown();
mojo::edk::SetDefaultProcessErrorCallback(
mojo::edk::ProcessErrorCallback());
}
using MockDeleteCallback = base::MockCallback<OutputStream::DeleteCallback>;
using MockBadMessageCallback =
......@@ -178,7 +183,7 @@ class TestEnvironment {
DISALLOW_COPY_AND_ASSIGN(TestEnvironment);
};
TEST(OutputStreamTest, ConstructDestruct) {
TEST(AudioServiceOutputStreamTest, ConstructDestruct) {
TestEnvironment env;
MockStream mock_stream;
EXPECT_CALL(env.created_callback(), Created(successfully_));
......@@ -204,7 +209,8 @@ TEST(OutputStreamTest, ConstructDestruct) {
base::RunLoop().RunUntilIdle();
}
TEST(OutputStreamTest, ConstructStreamAndDestructObserver_DestructsStream) {
TEST(AudioServiceOutputStreamTest,
ConstructStreamAndDestructObserver_DestructsStream) {
TestEnvironment env;
MockStream mock_stream;
env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating(
......@@ -231,7 +237,8 @@ TEST(OutputStreamTest, ConstructStreamAndDestructObserver_DestructsStream) {
Mock::VerifyAndClear(&env.client());
}
TEST(OutputStreamTest, ConstructStreamAndDestructClient_DestructsStream) {
TEST(AudioServiceOutputStreamTest,
ConstructStreamAndDestructClient_DestructsStream) {
TestEnvironment env;
MockStream mock_stream;
env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating(
......@@ -258,7 +265,8 @@ TEST(OutputStreamTest, ConstructStreamAndDestructClient_DestructsStream) {
Mock::VerifyAndClear(&env.observer());
}
TEST(OutputStreamTest, ConstructStreamAndReleaseStreamPtr_DestructsStream) {
TEST(AudioServiceOutputStreamTest,
ConstructStreamAndReleaseStreamPtr_DestructsStream) {
TestEnvironment env;
MockStream mock_stream;
env.audio_manager().SetMakeOutputStreamCB(base::BindRepeating(
......@@ -287,7 +295,7 @@ TEST(OutputStreamTest, ConstructStreamAndReleaseStreamPtr_DestructsStream) {
Mock::VerifyAndClear(&env.observer());
}
TEST(OutputStreamTest, Play_Plays) {
TEST(AudioServiceOutputStreamTest, Play_Plays) {
TestEnvironment env;
MockStream mock_stream;
EXPECT_CALL(env.created_callback(), Created(successfully_));
......@@ -325,7 +333,7 @@ TEST(OutputStreamTest, Play_Plays) {
base::RunLoop().RunUntilIdle();
}
TEST(OutputStreamTest, PlayAndPause_PlaysAndStops) {
TEST(AudioServiceOutputStreamTest, PlayAndPause_PlaysAndStops) {
TestEnvironment env;
MockStream mock_stream;
EXPECT_CALL(env.created_callback(), Created(successfully_));
......@@ -368,7 +376,7 @@ TEST(OutputStreamTest, PlayAndPause_PlaysAndStops) {
base::RunLoop().RunUntilIdle();
}
TEST(OutputStreamTest, SetVolume_SetsVolume) {
TEST(AudioServiceOutputStreamTest, SetVolume_SetsVolume) {
double new_volume = 0.618;
TestEnvironment env;
MockStream mock_stream;
......@@ -399,7 +407,7 @@ TEST(OutputStreamTest, SetVolume_SetsVolume) {
base::RunLoop().RunUntilIdle();
}
TEST(OutputStreamTest, SetNegativeVolume_BadMessage) {
TEST(AudioServiceOutputStreamTest, SetNegativeVolume_BadMessage) {
TestEnvironment env;
MockStream mock_stream;
EXPECT_CALL(env.created_callback(), Created(successfully_));
......@@ -424,7 +432,7 @@ TEST(OutputStreamTest, SetNegativeVolume_BadMessage) {
base::RunLoop().RunUntilIdle();
}
TEST(OutputStreamTest, SetVolumeGreaterThanOne_BadMessage) {
TEST(AudioServiceOutputStreamTest, SetVolumeGreaterThanOne_BadMessage) {
TestEnvironment env;
MockStream mock_stream;
EXPECT_CALL(env.created_callback(), Created(successfully_));
......@@ -449,7 +457,8 @@ TEST(OutputStreamTest, SetVolumeGreaterThanOne_BadMessage) {
base::RunLoop().RunUntilIdle();
}
TEST(OutputStreamTest, ConstructWithStreamCreationFailure_SignalsError) {
TEST(AudioServiceOutputStreamTest,
ConstructWithStreamCreationFailure_SignalsError) {
TestEnvironment env;
// By default, the MockAudioManager fails to create a stream.
......@@ -466,7 +475,7 @@ TEST(OutputStreamTest, ConstructWithStreamCreationFailure_SignalsError) {
Mock::VerifyAndClear(&env.observer());
}
TEST(OutputStreamTest,
TEST(AudioServiceOutputStreamTest,
ConstructWithStreamCreationFailureAndDestructBeforeErrorFires_NoCrash) {
// The main purpose of this test is to make sure that that delete callback
// call is deferred, and that it is canceled in case of destruction.
......
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