#include "media/audio/audio_device_stats_reporter.h"
#include "base/check.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
namespace media {
AudioDeviceStatsReporter::AudioDeviceStatsReporter(
const AudioParameters& params,
Type type)
: callback_duration_(params.GetBufferDuration()),
delay_log_callback_(CreateRealtimeCallback(
"Delay",
params.latency_tag(),
1000,
50,
type)),
glitch_count_log_callback_(CreateAggregateCallback(
"GlitchCount",
params.latency_tag(),
1000,
50,
type)),
glitch_duration_log_callback_(CreateAggregateCallback(
"GlitchDuration",
params.latency_tag(),
1000,
50,
type)),
discarded_first_callback_(
type != Type::kOutput)
{
CHECK(params.IsValid());
}
void AudioDeviceStatsReporter::ReportCallback(
base::TimeDelta delay,
const media::AudioGlitchInfo& glitch_info) {
if (!discarded_first_callback_) {
discarded_first_callback_ = true;
return;
}
delay_log_callback_.Run(delay.InMilliseconds());
++stats_.callback_count;
stats_.glitch_count += glitch_info.count;
stats_.glitch_duration += glitch_info.duration;
if (stats_.callback_count >= 1000) {
UploadStats(stats_, SamplingPeriod::kIntervals);
stats_ = {};
stream_is_short_ = false;
}
}
AudioDeviceStatsReporter::~AudioDeviceStatsReporter() {
if (stream_is_short_ && stats_.callback_count > 0) {
UploadStats(stats_, SamplingPeriod::kShort);
}
}
void AudioDeviceStatsReporter::UploadStats(const Stats& stats,
SamplingPeriod sampling_period) {
base::TimeDelta stats_duration = callback_duration_ * stats.callback_count;
DCHECK(stats_duration.is_positive());
int glitch_duration_permille =
std::round(1000 * stats.glitch_duration / stats_duration);
glitch_count_log_callback_.Run(stats.glitch_count, sampling_period);
glitch_duration_log_callback_.Run(glitch_duration_permille, sampling_period);
}
AudioDeviceStatsReporter::AggregateLogCallback
AudioDeviceStatsReporter::CreateAggregateCallback(
const std::string& stat_name,
media::AudioLatency::Type latency,
int max_value,
size_t bucket_count,
Type type) {
std::string base_name(base::StrCat(
{type == Type::kOutput ? "Media.AudioOutputDevice.AudioService"
: "Media.AudioInputDevice.AudioService",
stat_name}));
std::string short_name(base::StrCat({base_name, ".Short"}));
std::string intervals_name(base::StrCat({base_name, ".Intervals"}));
if (type == Type::kInput) {
return base::BindRepeating(
[](int max_value, size_t bucket_count, const std::string& short_name,
const std::string& intervals_name, int value,
SamplingPeriod sampling_period) {
if (sampling_period == SamplingPeriod::kShort) {
base::UmaHistogramCustomCounts(short_name, value, 1, max_value,
bucket_count);
} else {
base::UmaHistogramCustomCounts(intervals_name, value, 1, max_value,
bucket_count);
}
},
max_value, bucket_count, std::move(short_name),
std::move(intervals_name));
}
std::string short_with_latency_name(
base::StrCat({short_name, ".", AudioLatency::ToString(latency)}));
std::string intervals_with_latency_name(
base::StrCat({intervals_name, ".", AudioLatency::ToString(latency)}));
return base::BindRepeating(
[](int max_value, size_t bucket_count, const std::string& short_name,
const std::string& intervals_name,
const std::string& short_with_latency_name,
const std::string& intervals_with_latency_name, int value,
SamplingPeriod sampling_period) {
if (sampling_period == SamplingPeriod::kShort) {
base::UmaHistogramCustomCounts(short_name, value, 1, max_value,
bucket_count);
base::UmaHistogramCustomCounts(short_with_latency_name, value, 1,
max_value, bucket_count);
} else {
base::UmaHistogramCustomCounts(intervals_name, value, 1, max_value,
bucket_count);
base::UmaHistogramCustomCounts(intervals_with_latency_name, value, 1,
max_value, bucket_count);
}
},
max_value, bucket_count, std::move(short_name), std::move(intervals_name),
std::move(short_with_latency_name),
std::move(intervals_with_latency_name));
}
AudioDeviceStatsReporter::RealtimeLogCallback
AudioDeviceStatsReporter::CreateRealtimeCallback(
const std::string& stat_name,
media::AudioLatency::Type latency,
int max_value,
size_t bucket_count,
Type type) {
std::string base_name(base::StrCat(
{type == Type::kOutput ? "Media.AudioOutputDevice.AudioService"
: "Media.AudioInputDevice.AudioService",
stat_name}));
std::string base_with_latency_name(
base::StrCat({base_name, ".", AudioLatency::ToString(latency)}));
base::HistogramBase* histogram = base::Histogram::FactoryGet(
std::move(base_name), 1, max_value, bucket_count,
base::HistogramBase::kUmaTargetedHistogramFlag);
if (type == Type::kInput) {
return base::BindRepeating([](base::HistogramBase* histogram,
int value) { histogram->Add(value); },
base::Unretained(histogram));
}
base::HistogramBase* histogram_with_latency = base::Histogram::FactoryGet(
std::move(base_with_latency_name), 1, max_value, bucket_count,
base::HistogramBase::kUmaTargetedHistogramFlag);
return base::BindRepeating(
[](base::HistogramBase* histogram,
base::HistogramBase* histogram_with_latency, int value) {
histogram->Add(value);
histogram_with_latency->Add(value);
},
base::Unretained(histogram), base::Unretained(histogram_with_latency));
}
}