#include "services/audio/loopback_reference_manager.h"
#include <memory>
#include "base/containers/flat_set.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/task/bind_post_task.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "media/audio/audio_device_description.h"
#include "media/audio/audio_io.h"
#include "media/audio/system_glitch_reporter.h"
#include "media/base/audio_bus.h"
#include "services/audio/audio_manager_power_user.h"
#include "services/audio/reference_signal_provider.h"
namespace audio {
namespace {
using ReferenceOpenOutcome = ReferenceSignalProvider::ReferenceOpenOutcome;
using OpenOutcome = media::AudioInputStream::OpenOutcome;
ReferenceOpenOutcome MapStreamOpenOutcomeToReferenceOpenOutcome(
OpenOutcome outcome) {
switch (outcome) {
case OpenOutcome::kSuccess:
return ReferenceOpenOutcome::SUCCESS;
case OpenOutcome::kFailedSystemPermissions:
return ReferenceOpenOutcome::STREAM_OPEN_SYSTEM_PERMISSIONS_ERROR;
case OpenOutcome::kFailedInUse:
return ReferenceOpenOutcome::STREAM_OPEN_DEVICE_IN_USE_ERROR;
default:
return ReferenceOpenOutcome::STREAM_OPEN_ERROR;
}
}
ReferenceOpenOutcome ReportOpenResult(ReferenceOpenOutcome outcome) {
base::UmaHistogramEnumeration("Media.Audio.LoopbackReference.OpenResult",
outcome);
return outcome;
}
}
class LoopbackReferenceStreamIdProvider {
public:
LoopbackReferenceStreamIdProvider() = default;
~LoopbackReferenceStreamIdProvider() = default;
LoopbackReferenceStreamIdProvider(const LoopbackReferenceStreamIdProvider&) =
delete;
LoopbackReferenceStreamIdProvider& operator=(
const LoopbackReferenceStreamIdProvider&) = delete;
int GetId() {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
return next_stream_id_;
}
int GetNextId() {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
return next_stream_id_++;
}
private:
SEQUENCE_CHECKER(owning_sequence_);
int next_stream_id_ = 1000000;
};
class LoopbackReferenceManagerCore
: public media::AudioInputStream::AudioInputCallback {
public:
using ErrorCallback = base::OnceCallback<void()>;
explicit LoopbackReferenceManagerCore(
media::AudioManager* audio_manager,
LoopbackReferenceStreamIdProvider* stream_id_provider,
ErrorCallback on_error_callback)
: audio_manager_(audio_manager),
glitch_reporter_(
media::SystemGlitchReporter::StreamType::kLoopbackReference),
task_runner_(base::SequencedTaskRunner::GetCurrentDefault()),
stream_id_provider_(stream_id_provider),
on_error_callback_(std::move(on_error_callback)) {}
LoopbackReferenceManagerCore(const LoopbackReferenceManagerCore&) = delete;
LoopbackReferenceManagerCore& operator=(const LoopbackReferenceManagerCore&) =
delete;
~LoopbackReferenceManagerCore() override {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
EnsureLoopbackStreamClosed();
bool had_runtime_error = !on_error_callback_;
base::UmaHistogramBoolean("Media.Audio.LoopbackReference.HadRuntimeError",
had_runtime_error);
if (had_runtime_error) {
for (ReferenceOutput::Listener* listener : listeners_) {
listener->OnReferenceStreamError();
}
}
}
void SendLogMessage(const std::string& message) {
if (!audio_log_) {
return;
}
audio_log_->OnLogMessage(base::StringPrintf(
"LRMC::%s [id=%u] [this=0x%" PRIXPTR "]", message.c_str(),
stream_id_provider_->GetId(), reinterpret_cast<uintptr_t>(this)));
}
void ReportAndResetGlitchStats() {
media::SystemGlitchReporter::Stats stats =
glitch_reporter_.GetLongTermStatsAndReset();
std::string formatted_message = base::StringPrintf(
"%s => (num_glitches_detected=[%d], cumulative_audio_lost=[%llu ms], "
"largest_glitch=[%llu ms])",
__func__, stats.glitches_detected,
stats.total_glitch_duration.InMilliseconds(),
stats.largest_glitch_duration.InMilliseconds());
SendLogMessage(formatted_message);
}
ReferenceOpenOutcome StartListening(ReferenceOutput::Listener* listener,
const std::string& device_id) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (loopback_stream_) {
base::AutoLock scoped_lock(lock_);
listeners_.insert(listener);
return ReferenceOpenOutcome::SUCCESS;
}
const std::string loopback_device_id =
media::AudioDeviceDescription::kLoopbackAllDevicesId;
const media::AudioParameters params =
AudioManagerPowerUser(audio_manager_)
.GetInputStreamParameters(loopback_device_id);
sample_rate_ = params.sample_rate();
audio_log_ = audio_manager_->CreateAudioLog(
media::AudioLogFactory::AudioComponent::kAudioInputController,
stream_id_provider_->GetNextId());
SendLogMessage(
base::StrCat({"StartListening(device_id=", loopback_device_id, ")"}));
loopback_stream_ = audio_manager_->MakeAudioInputStream(
params, loopback_device_id,
base::BindRepeating(&media::AudioLog::OnLogMessage,
base::Unretained(audio_log_.get())));
if (!loopback_stream_) {
return ReferenceOpenOutcome::STREAM_CREATE_ERROR;
}
media::AudioInputStream::OpenOutcome stream_open_outcome =
loopback_stream_->Open();
if (stream_open_outcome != OpenOutcome::kSuccess) {
loopback_stream_.ExtractAsDangling()->Close();
return MapStreamOpenOutcomeToReferenceOpenOutcome(stream_open_outcome);
}
audio_log_->OnCreated(params, loopback_device_id);
{
base::AutoLock scoped_lock(lock_);
listeners_.insert(listener);
}
loopback_stream_->Start(this);
audio_log_->OnStarted();
return ReferenceOpenOutcome::SUCCESS;
}
void StopListening(ReferenceOutput::Listener* listener) {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
SendLogMessage("StopListening()");
bool is_empty;
{
base::AutoLock scoped_lock(lock_);
listeners_.erase(listener);
is_empty = listeners_.empty();
}
if (is_empty) {
EnsureLoopbackStreamClosed();
}
}
base::WeakPtr<LoopbackReferenceManagerCore> GetWeakPtr() {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
return weak_ptr_factory_.GetWeakPtr();
}
void OnData(const media::AudioBus* source,
base::TimeTicks capture_time,
double volume,
const media::AudioGlitchInfo& audio_glitch_info) override {
base::AutoLock scoped_lock(lock_);
for (ReferenceOutput::Listener* listener : listeners_) {
listener->OnPlayoutData(*source, sample_rate_,
base::TimeDelta());
}
glitch_reporter_.UpdateStats(audio_glitch_info.duration);
}
void OnError() override {
task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&LoopbackReferenceManagerCore::OnErrorMainSequence,
weak_ptr_factory_.GetWeakPtr()));
}
void OnErrorMainSequence() {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
SendLogMessage("OnError()");
if (on_error_callback_) {
std::move(on_error_callback_).Run();
}
}
private:
void EnsureLoopbackStreamClosed() {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
if (!loopback_stream_) {
return;
}
loopback_stream_->Stop();
ReportAndResetGlitchStats();
audio_log_->OnStopped();
loopback_stream_.ExtractAsDangling()->Close();
audio_log_->OnClosed();
audio_log_.reset();
}
const raw_ptr<media::AudioManager> audio_manager_;
media::SystemGlitchReporter glitch_reporter_;
const scoped_refptr<base::SequencedTaskRunner> task_runner_;
const raw_ptr<LoopbackReferenceStreamIdProvider> stream_id_provider_;
ErrorCallback on_error_callback_;
raw_ptr<media::AudioInputStream> loopback_stream_ = nullptr;
std::unique_ptr<media::AudioLog> audio_log_;
int sample_rate_;
base::Lock lock_;
base::flat_set<ReferenceOutput::Listener*> listeners_ GUARDED_BY(lock_);
base::WeakPtrFactory<LoopbackReferenceManagerCore> weak_ptr_factory_{this};
};
class LoopbackReferenceProvider : public ReferenceSignalProvider {
public:
LoopbackReferenceProvider(base::WeakPtr<LoopbackReferenceManagerCore> core)
: core_(core) {}
ReferenceSignalProvider::Type GetType() const final {
return ReferenceSignalProvider::Type::kLoopbackReference;
}
ReferenceOpenOutcome StartListening(ReferenceOutput::Listener* listener,
const std::string& device_id) final {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
if (core_) {
return ReportOpenResult(core_->StartListening(listener, device_id));
}
return ReportOpenResult(ReferenceOpenOutcome::STREAM_PREVIOUS_ERROR);
}
void StopListening(ReferenceOutput::Listener* listener) final {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
if (core_) {
core_->StopListening(listener);
}
}
private:
SEQUENCE_CHECKER(owning_sequence_);
base::WeakPtr<LoopbackReferenceManagerCore> core_;
};
LoopbackReferenceManager::LoopbackReferenceManager(
media::AudioManager* audio_manager)
: audio_manager_(audio_manager),
stream_id_provider_(
std::make_unique<LoopbackReferenceStreamIdProvider>()) {}
std::unique_ptr<ReferenceSignalProvider>
LoopbackReferenceManager::GetReferenceSignalProvider() {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
if (!core_) {
core_ = std::make_unique<LoopbackReferenceManagerCore>(
audio_manager_, stream_id_provider_.get(),
base::BindOnce(&LoopbackReferenceManager::OnCoreError,
weak_ptr_factory_.GetWeakPtr()));
}
return std::make_unique<LoopbackReferenceProvider>(core_->GetWeakPtr());
}
void LoopbackReferenceManager::OnCoreError() {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
CHECK(core_);
core_.reset();
}
LoopbackReferenceManager::~LoopbackReferenceManager() {
DCHECK_CALLED_ON_VALID_SEQUENCE(owning_sequence_);
}
}