Commit 93317539 authored by Jeroen Dhollander's avatar Jeroen Dhollander Committed by Chromium LUCI CQ

Implement AudioInputController in Libassistant mojom service

This CL migrates the implementation of |AudioInputProviderImpl| and
|AudioInputImpl| to the new Libassistant mojom service.

To reduce the size of the CL this migration has been split in 3 CLs:
   1. The mojom APIs
   2. The implementation on the Libassistant mojom service (this CL).
   3. Changing the Browser side to use the new mojom service.

This does mean that this CL has to copy |AudioInputImpl|,
|AudioInputProviderImpl| and |AudioInputStream|, rather than move them.
In a follow up CL these classes will be removed from
//chromeos/services/assistant, when they are unused there.

These classes are mostly unchanged, except:
   - Renamed |AudioStream| to |AudioInputStream|.
   - Replaced |AudioStreamFactoryDelegate| with
     |mojom::AudioStreamFactoryDelegate|.
   - Stripped support for the gn flag
     `enable_fake_assistant_microphone`, as that would have required
     some extra files to be moved (and this CL is big enough as-is).
     I will re-add this in a follow up CL.
     Note that the fake microphone still works, as this new version is
     not used anyway.
   - An extra VLOG in UpdateRecordingState().

Bug: b/171748795
Test: chromeos_unittests --gtest_filter="Audio*"
Cq-Include-Trybots: luci.chrome.try:linux-chromeos-chrome
Change-Id: I80965e5de31ea48c19363966bae384414b16bcc8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2605706
Commit-Queue: Jeroen Dhollander <jeroendh@chromium.org>
Auto-Submit: Jeroen Dhollander <jeroendh@chromium.org>
Reviewed-by: default avatarXiaohui Chen <xiaohuic@chromium.org>
Reviewed-by: default avatarDale Curtis <dalecurtis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#844206}
parent 41f8b5d8
......@@ -33,6 +33,14 @@ source_set("internal") {
sources = [
"assistant_manager_observer.h",
"audio/audio_input_impl.cc",
"audio/audio_input_impl.h",
"audio/audio_input_provider_impl.cc",
"audio/audio_input_provider_impl.h",
"audio/audio_input_stream.cc",
"audio/audio_input_stream.h",
"audio_input_controller.cc",
"audio_input_controller.h",
"conversation_controller.cc",
"conversation_controller.h",
"platform_api.cc",
......@@ -52,7 +60,12 @@ source_set("internal") {
"//chromeos/services/assistant/public/cpp/migration",
"//chromeos/services/libassistant/public/mojom",
"//libassistant/shared/internal_api:assistant_manager_internal",
"//libassistant/shared/internal_api:fuchsia_api_helper",
"//libassistant/shared/internal_api/c:api_wrappers_entrypoint",
"//libassistant/shared/public",
"//libassistant/shared/public:export",
"//media",
"//services/audio/public/cpp",
]
defines = [ "IS_LIBASSISTANT_SERVICE_IMPL" ]
......@@ -60,7 +73,10 @@ source_set("internal") {
source_set("unit_tests") {
testonly = true
sources = [ "service_controller_unittest.cc" ]
sources = [
"audio_input_controller_unittest.cc",
"service_controller_unittest.cc",
]
deps = [
":internal",
......@@ -73,6 +89,7 @@ source_set("unit_tests") {
"//chromeos/services/assistant/public/cpp/migration:test_support",
"//chromeos/services/libassistant/public/mojom",
"//libassistant/shared/internal_api:assistant_manager_internal",
"//services/audio/public/cpp:test_support",
"//testing/gmock",
"//testing/gtest",
]
......
include_rules = [
"+libassistant",
"+media/audio",
"+media/base",
"+services/audio/public",
]
This diff is collapsed.
// Copyright 2021 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 CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_INPUT_IMPL_H_
#define CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_INPUT_IMPL_H_
#include <memory>
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/optional.h"
#include "base/sequence_checker.h"
#include "base/synchronization/lock.h"
#include "base/time/time.h"
#include "chromeos/services/assistant/public/cpp/assistant_service.h"
#include "chromeos/services/libassistant/public/mojom/audio_input_controller.mojom.h"
#include "libassistant/shared/public/platform_audio_input.h"
#include "media/base/audio_capturer_source.h"
#include "mojo/public/cpp/bindings/remote.h"
namespace chromeos {
namespace libassistant {
class AudioInputStream;
class COMPONENT_EXPORT(LIBASSISTANT_SERVICE) AudioInputImpl
: public assistant_client::AudioInput,
public media::AudioCapturerSource::CaptureCallback {
public:
explicit AudioInputImpl(const base::Optional<std::string>& device_id);
AudioInputImpl(const AudioInputImpl&) = delete;
AudioInputImpl& operator=(const AudioInputImpl&) = delete;
~AudioInputImpl() override;
class HotwordStateManager {
public:
explicit HotwordStateManager(AudioInputImpl* audio_input_);
HotwordStateManager(const HotwordStateManager&) = delete;
HotwordStateManager& operator=(const HotwordStateManager&) = delete;
virtual ~HotwordStateManager() = default;
virtual void OnConversationTurnStarted() {}
virtual void OnConversationTurnFinished() {}
virtual void OnCaptureDataArrived() {}
virtual void RecreateAudioInputStream();
protected:
AudioInputImpl* input_;
};
void RecreateStateManager();
void Bind(mojo::PendingRemote<mojom::AudioStreamFactoryDelegate> delegate);
// media::AudioCapturerSource::CaptureCallback overrides:
void Capture(const media::AudioBus* audio_source,
base::TimeTicks audio_capture_time,
double volume,
bool key_pressed) override;
void OnCaptureError(const std::string& message) override;
void OnCaptureMuted(bool is_muted) override;
// assistant_client::AudioInput overrides. These function are called by
// assistant from assistant thread, for which we should not assume any
// //base related thread context to be in place.
assistant_client::BufferFormat GetFormat() const override;
void AddObserver(assistant_client::AudioInput::Observer* observer) override;
void RemoveObserver(
assistant_client::AudioInput::Observer* observer) override;
// Called when the mic state associated with the interaction is changed.
void SetMicState(bool mic_open);
void OnConversationTurnStarted();
void OnConversationTurnFinished();
// Called when hotword enabled status changed.
void OnHotwordEnabled(bool enable);
void SetDeviceId(const base::Optional<std::string>& device_id);
void SetHotwordDeviceId(const base::Optional<std::string>& device_id);
// Called when the user opens/closes the lid.
void OnLidStateChanged(mojom::LidState new_state);
void RecreateAudioInputStream(bool use_dsp);
bool IsHotwordAvailable() const;
// Returns the recording state used in unittests.
bool IsRecordingForTesting() const;
// Returns if the hotword device is used for recording now.
bool IsUsingHotwordDeviceForTesting() const;
// Returns the id of the device that is currently recording audio.
// Returns nullopt if no audio is being recorded.
base::Optional<std::string> GetOpenDeviceIdForTesting() const;
// Returns if dead stream detection is being used for the current audio
// recording. Returns nullopt if no audio is being recorded.
base::Optional<bool> IsUsingDeadStreamDetectionForTesting() const;
private:
void StartRecording();
void StopRecording();
void UpdateRecordingState();
std::string GetDeviceId(bool use_dsp) const;
bool ShouldEnableDeadStreamDetection(bool use_dsp) const;
// User explicitly requested to open microphone.
bool mic_open_ = false;
// Whether hotword is currently enabled.
bool hotword_enabled_ = true;
// Guards observers_;
base::Lock lock_;
std::vector<assistant_client::AudioInput::Observer*> observers_;
// This is the total number of frames captured during the life time of this
// object. We don't worry about overflow because this count is only used for
// logging purposes. If in the future this changes, we should re-evaluate.
int captured_frames_count_ = 0;
base::TimeTicks last_frame_count_report_time_;
// To be initialized on assistant thread the first call to AddObserver.
// It ensures that AddObserver / RemoveObserver are called on the same
// sequence.
SEQUENCE_CHECKER(observer_sequence_checker_);
scoped_refptr<base::SequencedTaskRunner> task_runner_;
std::unique_ptr<HotwordStateManager> state_manager_;
mojo::Remote<mojom::AudioStreamFactoryDelegate>
audio_stream_factory_delegate_;
// Preferred audio input device which will be used for capture.
base::Optional<std::string> preferred_device_id_;
// Hotword input device used for hardware based hotword detection.
base::Optional<std::string> hotword_device_id_;
// Currently open audio stream. nullptr if no audio stream is open.
std::unique_ptr<AudioInputStream> open_audio_stream_;
// Start with lidstate |kClosed| so we do not open the microphone before we
// know if the lid is open or closed.
mojom::LidState lid_state_ = mojom::LidState ::kClosed;
base::WeakPtrFactory<AudioInputImpl> weak_factory_;
};
} // namespace libassistant
} // namespace chromeos
#endif // CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_INPUT_IMPL_H_
// Copyright 2021 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 "chromeos/services/libassistant/audio/audio_input_provider_impl.h"
#include "base/time/time.h"
#include "chromeos/services/assistant/public/cpp/features.h"
namespace chromeos {
namespace libassistant {
AudioInputProviderImpl::AudioInputProviderImpl()
: audio_input_(/*device_id=*/base::nullopt) {}
AudioInputProviderImpl::~AudioInputProviderImpl() = default;
AudioInputImpl& AudioInputProviderImpl::GetAudioInput() {
return audio_input_;
}
int64_t AudioInputProviderImpl::GetCurrentAudioTime() {
if (chromeos::assistant::features::IsAudioEraserEnabled())
return base::TimeTicks::Now().since_origin().InMicroseconds();
return 0;
}
} // namespace libassistant
} // namespace chromeos
// Copyright 2021 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 CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_INPUT_PROVIDER_IMPL_H_
#define CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_INPUT_PROVIDER_IMPL_H_
#include <cstdint>
#include <memory>
#include "base/macros.h"
#include "chromeos/services/libassistant/audio/audio_input_impl.h"
#include "libassistant/shared/public/platform_audio_input.h"
namespace chromeos {
namespace libassistant {
class AudioInputProviderImpl : public assistant_client::AudioInputProvider {
public:
AudioInputProviderImpl();
AudioInputProviderImpl(const AudioInputProviderImpl&) = delete;
AudioInputProviderImpl& operator=(const AudioInputProviderImpl&) = delete;
~AudioInputProviderImpl() override;
// assistant_client::AudioInputProvider overrides:
AudioInputImpl& GetAudioInput() override;
int64_t GetCurrentAudioTime() override;
private:
AudioInputImpl audio_input_;
};
} // namespace libassistant
} // namespace chromeos
#endif // CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_INPUT_PROVIDER_IMPL_H_
// Copyright 2021 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 "chromeos/services/libassistant/audio/audio_input_stream.h"
#include "base/notreached.h"
#include "chromeos/services/libassistant/public/mojom/audio_input_controller.mojom.h"
namespace chromeos {
namespace libassistant {
namespace {
audio::DeadStreamDetection ToDeadStreamDetection(bool detect_dead_stream) {
return detect_dead_stream ? audio::DeadStreamDetection::kEnabled
: audio::DeadStreamDetection::kDisabled;
}
} // namespace
AudioInputStream::AudioInputStream(
mojom::AudioStreamFactoryDelegate* delegate,
const std::string& device_id,
bool detect_dead_stream,
assistant_client::BufferFormat buffer_format,
media::AudioCapturerSource::CaptureCallback* capture_callback)
: device_id_(device_id),
detect_dead_stream_(detect_dead_stream),
buffer_format_(buffer_format),
delegate_(delegate),
capture_callback_(capture_callback) {
Start();
}
AudioInputStream::~AudioInputStream() {
Stop();
}
void AudioInputStream::Start() {
delegate_->GetAudioStreamFactory(
base::BindOnce(&AudioInputStream::OnAudioSteamFactoryReady,
weak_ptr_factory_.GetWeakPtr()));
}
void AudioInputStream::OnAudioSteamFactoryReady(
mojo::PendingRemote<audio::mojom::StreamFactory> audio_stream_factory) {
if (!audio_stream_factory.is_valid())
return;
source_ =
audio::CreateInputDevice(std::move(audio_stream_factory), device_id(),
ToDeadStreamDetection(detect_dead_stream_));
source_->Initialize(GetAudioParameters(), capture_callback_);
source_->Start();
}
void AudioInputStream::Stop() {
if (source_) {
source_->Stop();
source_.reset();
}
}
media::AudioParameters AudioInputStream::GetAudioParameters() const {
// AUDIO_PCM_LINEAR and AUDIO_PCM_LOW_LATENCY are the same on CRAS.
auto result = media::AudioParameters(
media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
media::GuessChannelLayout(buffer_format_.num_channels),
buffer_format_.sample_rate,
buffer_format_.sample_rate / 10 /* buffer size for 100 ms */);
// Set the HOTWORD mask so CRAS knows the device is used for HOTWORD purpose
// and is able to conduct the tuning specifically for the scenario. Whether
// the HOTWORD is conducted by a hotword device or other devices like
// internal mic will be determined by the device_id passed to CRAS.
result.set_effects(media::AudioParameters::PlatformEffectsMask::HOTWORD);
return result;
}
} // namespace libassistant
} // namespace chromeos
// Copyright 2021 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 CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_INPUT_STREAM_H_
#define CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_INPUT_STREAM_H_
#include <string>
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/services/libassistant/public/mojom/audio_input_controller.mojom-forward.h"
#include "libassistant/shared/public/platform_audio_buffer.h"
#include "media/base/audio_capturer_source.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "services/audio/public/cpp/device_factory.h"
#include "services/audio/public/mojom/stream_factory.mojom.h"
namespace chromeos {
namespace libassistant {
// A single audio stream. All captured packets will be sent to the given
// capture callback.
// The audio stream will be opened as soon as this class is created, and
// will be closed in the destructor.
class AudioInputStream {
public:
AudioInputStream(
mojom::AudioStreamFactoryDelegate* delegate,
const std::string& device_id,
bool detect_dead_stream,
assistant_client::BufferFormat buffer_format,
media::AudioCapturerSource::CaptureCallback* capture_callback);
AudioInputStream(AudioInputStream&) = delete;
AudioInputStream& operator=(AudioInputStream&) = delete;
~AudioInputStream();
const std::string& device_id() const { return device_id_; }
bool has_dead_stream_detection() const { return detect_dead_stream_; }
private:
void Start();
void OnAudioSteamFactoryReady(
mojo::PendingRemote<audio::mojom::StreamFactory> audio_stream_factory);
void Stop();
media::AudioParameters GetAudioParameters() const;
// Device used for recording.
std::string device_id_;
bool detect_dead_stream_;
assistant_client::BufferFormat buffer_format_;
mojom::AudioStreamFactoryDelegate* const delegate_;
media::AudioCapturerSource::CaptureCallback* const capture_callback_;
scoped_refptr<media::AudioCapturerSource> source_;
base::WeakPtrFactory<AudioInputStream> weak_ptr_factory_{this};
};
} // namespace libassistant
} // namespace chromeos
#endif // CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_AUDIO_INPUT_STREAM_H_
// Copyright 2021 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 "chromeos/services/libassistant/audio_input_controller.h"
#include "base/notreached.h"
namespace chromeos {
namespace libassistant {
AudioInputController::AudioInputController() = default;
AudioInputController::~AudioInputController() = default;
void AudioInputController::Bind(
mojo::PendingReceiver<mojom::AudioInputController> receiver,
mojo::PendingRemote<mojom::AudioStreamFactoryDelegate> delegate) {
receiver_.Bind(std::move(receiver));
audio_input().Bind(std::move(delegate));
}
void AudioInputController::SetMicOpen(bool mic_open) {
audio_input().SetMicState(mic_open);
}
void AudioInputController::SetHotwordEnabled(bool enable) {
audio_input().OnHotwordEnabled(enable);
}
void AudioInputController::SetDeviceId(
const base::Optional<std::string>& device_id) {
DCHECK(device_id != "");
audio_input().SetDeviceId(device_id);
}
void AudioInputController::SetHotwordDeviceId(
const base::Optional<std::string>& device_id) {
DCHECK(device_id != "");
audio_input().SetHotwordDeviceId(device_id);
}
void AudioInputController::SetLidState(mojom::LidState new_state) {
audio_input().OnLidStateChanged(new_state);
}
void AudioInputController::OnConversationTurnStarted() {
audio_input().OnConversationTurnStarted();
}
void AudioInputController::OnConversationTurnFinished() {
audio_input().OnConversationTurnFinished();
}
AudioInputImpl& AudioInputController::audio_input() {
return audio_input_provider().GetAudioInput();
}
} // namespace libassistant
} // namespace chromeos
// Copyright 2021 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 CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_INPUT_CONTROLLER_H_
#define CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_INPUT_CONTROLLER_H_
#include "chromeos/services/libassistant/audio/audio_input_provider_impl.h"
#include "chromeos/services/libassistant/public/mojom/audio_input_controller.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver.h"
namespace chromeos {
namespace libassistant {
// Implementation of |mojom::AudioInputController| that will forward all calls
// to a Libassistant V1 |assistant_client::AudioInputProvider| implementation.
class COMPONENT_EXPORT(LIBASSISTANT_SERVICE) AudioInputController
: public mojom::AudioInputController {
public:
AudioInputController();
AudioInputController(AudioInputController&) = delete;
AudioInputController& operator=(AudioInputController&) = delete;
~AudioInputController() override;
void Bind(mojo::PendingReceiver<mojom::AudioInputController> receiver,
mojo::PendingRemote<mojom::AudioStreamFactoryDelegate> delegate);
// mojom::AudioInputController implementation:
void SetMicOpen(bool mic_open) override;
void SetHotwordEnabled(bool enable) override;
void SetDeviceId(const base::Optional<std::string>& device_id) override;
void SetHotwordDeviceId(
const base::Optional<std::string>& device_id) override;
void SetLidState(mojom::LidState new_state) override;
void OnConversationTurnStarted() override;
void OnConversationTurnFinished() override;
AudioInputProviderImpl& audio_input_provider() {
return audio_input_provider_;
}
private:
AudioInputImpl& audio_input();
mojo::Receiver<mojom::AudioInputController> receiver_{this};
AudioInputProviderImpl audio_input_provider_;
};
} // namespace libassistant
} // namespace chromeos
#endif // CHROMEOS_SERVICES_LIBASSISTANT_AUDIO_INPUT_CONTROLLER_H_
......@@ -10,6 +10,7 @@
#include "base/check.h"
#include "base/logging.h"
#include "chromeos/services/assistant/public/cpp/migration/cros_platform_api.h"
#include "chromeos/services/libassistant/audio_input_controller.h"
#include "chromeos/services/libassistant/conversation_controller.h"
#include "chromeos/services/libassistant/platform_api.h"
#include "chromeos/services/libassistant/service_controller.h"
......@@ -26,7 +27,8 @@ LibassistantService::LibassistantService(
service_controller_(
std::make_unique<ServiceController>(delegate, platform_api_.get())),
conversation_controller_(
std::make_unique<ConversationController>(service_controller_.get())) {
std::make_unique<ConversationController>(service_controller_.get())),
audio_input_controller_(std::make_unique<AudioInputController>()) {
platform_api_->SetAudioInputProvider(&platform_api->GetAudioInputProvider())
.SetAudioOutputProvider(&platform_api->GetAudioOutputProvider())
.SetAuthProvider(&platform_api->GetAuthProvider())
......@@ -44,6 +46,8 @@ void LibassistantService::Bind(
mojo::PendingReceiver<mojom::ConversationController>
conversation_controller,
mojo::PendingReceiver<mojom::ServiceController> service_controller) {
audio_input_controller_->Bind(std::move(audio_input_controller),
std::move(audio_stream_factory_delegate));
service_controller_->Bind(std::move(service_controller));
conversation_controller_->Bind(std::move(conversation_controller));
}
......
......@@ -29,6 +29,7 @@ class CrosPlatformApi;
namespace chromeos {
namespace libassistant {
class AudioInputController;
class ConversationController;
class PlatformApi;
class ServiceController;
......@@ -67,6 +68,7 @@ class COMPONENT_EXPORT(LIBASSISTANT_SERVICE) LibassistantService
std::unique_ptr<PlatformApi> platform_api_;
std::unique_ptr<ServiceController> service_controller_;
std::unique_ptr<ConversationController> conversation_controller_;
std::unique_ptr<AudioInputController> audio_input_controller_;
};
} // namespace libassistant
......
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