blob: ec266a8802e5345b3d62b8e473ece409eaa00800 [file] [log] [blame]
// 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 "chromeos/services/assistant/platform/audio_output_provider_impl.h"
#include <algorithm>
#include <utility>
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/services/assistant/media_session/assistant_media_session.h"
#include "chromeos/services/assistant/platform/audio_stream_factory_delegate.h"
#include "chromeos/services/assistant/platform/audio_stream_handler.h"
#include "chromeos/services/assistant/public/cpp/assistant_client.h"
#include "chromeos/services/assistant/public/mojom/assistant_audio_decoder.mojom.h"
#include "libassistant/shared/public/platform_audio_buffer.h"
#include "media/audio/audio_device_description.h"
namespace chromeos {
namespace assistant {
namespace {
bool IsEncodedFormat(const assistant_client::OutputStreamFormat& format) {
return format.encoding ==
assistant_client::OutputStreamEncoding::STREAM_MP3 ||
format.encoding ==
assistant_client::OutputStreamEncoding::STREAM_OPUS_IN_OGG;
}
class AudioOutputImpl : public assistant_client::AudioOutput {
public:
AudioOutputImpl(
mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory,
scoped_refptr<base::SequencedTaskRunner> task_runner,
scoped_refptr<base::SequencedTaskRunner> background_task_runner,
mojom::AssistantAudioDecoderFactory* audio_decoder_factory,
AssistantMediaSession* media_session,
assistant_client::OutputStreamType type,
assistant_client::OutputStreamFormat format,
const std::string& device_id)
: stream_factory_(std::move(stream_factory)),
main_task_runner_(task_runner),
background_thread_task_runner_(background_task_runner),
audio_decoder_factory_(audio_decoder_factory),
media_session_(media_session),
stream_type_(type),
format_(format),
audio_stream_handler_(
std::make_unique<AudioStreamHandler>(task_runner)),
device_owner_(std::make_unique<AudioDeviceOwner>(task_runner,
background_task_runner,
device_id)) {}
~AudioOutputImpl() override {
// This ensures that it will be executed after StartOnMainThread.
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<AudioDeviceOwner> device_owner,
scoped_refptr<base::SequencedTaskRunner> background_runner) {
// Ensures |device_owner| is destructed on the correct thread.
background_runner->DeleteSoon(FROM_HERE, device_owner.release());
},
std::move(device_owner_), background_thread_task_runner_));
main_task_runner_->DeleteSoon(FROM_HERE, audio_stream_handler_.release());
}
// assistant_client::AudioOutput overrides:
assistant_client::OutputStreamType GetType() override { return stream_type_; }
void Start(assistant_client::AudioOutput::Delegate* delegate) override {
// TODO(llin): Remove getting audio focus here after libassistant handles
// acquiring audio focus for the internal media player.
if (stream_type_ == assistant_client::OutputStreamType::STREAM_MEDIA) {
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AssistantMediaSession::RequestAudioFocus,
media_session_->GetWeakPtr(),
media_session::mojom::AudioFocusType::kGain));
}
if (IsEncodedFormat(format_)) {
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
&AudioStreamHandler::StartAudioDecoder,
base::Unretained(audio_stream_handler_.get()),
audio_decoder_factory_, delegate,
base::BindOnce(&AudioDeviceOwner::StartOnMainThread,
base::Unretained(device_owner_.get()),
media_session_, audio_stream_handler_.get(),
std::move(stream_factory_))));
} else {
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AudioDeviceOwner::StartOnMainThread,
base::Unretained(device_owner_.get()), media_session_,
delegate, std::move(stream_factory_), format_));
}
}
void Stop() override {
// TODO(llin): Remove abandoning audio focus here after libassistant handles
// abandoning audio focus for the internal media player.
if (stream_type_ == assistant_client::OutputStreamType::STREAM_MEDIA) {
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AssistantMediaSession::AbandonAudioFocusIfNeeded,
media_session_->GetWeakPtr()));
}
if (IsEncodedFormat(format_)) {
device_owner_->SetDelegate(nullptr);
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AudioStreamHandler::OnStopped,
base::Unretained(audio_stream_handler_.get())));
} else {
background_thread_task_runner_->PostTask(
FROM_HERE, base::BindOnce(&AudioDeviceOwner::StopOnBackgroundThread,
base::Unretained(device_owner_.get())));
}
}
private:
mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory_;
scoped_refptr<base::SequencedTaskRunner> main_task_runner_;
scoped_refptr<base::SequencedTaskRunner> background_thread_task_runner_;
mojom::AssistantAudioDecoderFactory* audio_decoder_factory_;
AssistantMediaSession* media_session_;
const assistant_client::OutputStreamType stream_type_;
assistant_client::OutputStreamFormat format_;
std::unique_ptr<AudioStreamHandler> audio_stream_handler_;
std::unique_ptr<AudioDeviceOwner> device_owner_;
DISALLOW_COPY_AND_ASSIGN(AudioOutputImpl);
};
} // namespace
AudioOutputProviderImpl::AudioOutputProviderImpl(
AssistantMediaSession* media_session,
scoped_refptr<base::SequencedTaskRunner> background_task_runner,
const std::string& device_id)
: audio_stream_factory_delegate_(
std::make_unique<DefaultAudioStreamFactoryDelegate>()),
loop_back_input_(audio_stream_factory_delegate_.get(),
media::AudioDeviceDescription::kLoopbackInputDeviceId),
volume_control_impl_(media_session),
main_task_runner_(base::SequencedTaskRunnerHandle::Get()),
background_task_runner_(background_task_runner),
device_id_(device_id),
media_session_(media_session) {
AssistantClient::Get()->RequestAudioDecoderFactory(
audio_decoder_factory_remote_.BindNewPipeAndPassReceiver());
audio_decoder_factory_ = audio_decoder_factory_remote_.get();
}
AudioOutputProviderImpl::~AudioOutputProviderImpl() = default;
assistant_client::AudioOutput* AudioOutputProviderImpl::CreateAudioOutput(
assistant_client::OutputStreamType type,
const assistant_client::OutputStreamFormat& stream_format) {
mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory;
main_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&AudioOutputProviderImpl::BindStreamFactory,
weak_ptr_factory_.GetWeakPtr(),
stream_factory.InitWithNewPipeAndPassReceiver()));
// Owned by one arbitrary thread inside libassistant. It will be destroyed
// once assistant_client::AudioOutput::Delegate::OnStopped() is called.
return new AudioOutputImpl(std::move(stream_factory), main_task_runner_,
background_task_runner_, audio_decoder_factory_,
media_session_, type, stream_format, device_id_);
}
std::vector<assistant_client::OutputStreamEncoding>
AudioOutputProviderImpl::GetSupportedStreamEncodings() {
return std::vector<assistant_client::OutputStreamEncoding>{
assistant_client::OutputStreamEncoding::STREAM_PCM_S16,
assistant_client::OutputStreamEncoding::STREAM_PCM_S32,
assistant_client::OutputStreamEncoding::STREAM_PCM_F32,
assistant_client::OutputStreamEncoding::STREAM_MP3,
assistant_client::OutputStreamEncoding::STREAM_OPUS_IN_OGG,
};
}
assistant_client::AudioInput* AudioOutputProviderImpl::GetReferenceInput() {
return &loop_back_input_;
}
bool AudioOutputProviderImpl::SupportsPlaybackTimestamp() const {
// TODO(muyuanli): implement.
return false;
}
assistant_client::VolumeControl& AudioOutputProviderImpl::GetVolumeControl() {
return volume_control_impl_;
}
void AudioOutputProviderImpl::RegisterAudioEmittingStateCallback(
AudioEmittingStateCallback callback) {
// TODO(muyuanli): implement.
}
void AudioOutputProviderImpl::BindStreamFactory(
mojo::PendingReceiver<audio::mojom::StreamFactory> receiver) {
AssistantClient::Get()->RequestAudioStreamFactory(std::move(receiver));
}
} // namespace assistant
} // namespace chromeos