blob: 78e615f58e972f2317269820911cf4a7980ec7e7 [file] [log] [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "services/audio/input_glitch_counter.h"
#include <cstddef>
#include <utility>
#include "base/check_op.h"
#include "base/format_macros.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
namespace audio {
namespace {
// Used to log if any audio glitches have been detected during an audio session.
// Elements in this enum should not be added, deleted or rearranged.
enum class AudioGlitchResult {
kNoGlitches = 0,
kGlitches = 1,
kMaxValue = kGlitches
};
} // namespace
InputGlitchCounter::InputGlitchCounter(
base::RepeatingCallback<void(const std::string&)> log_callback)
: log_callback_(std::move(log_callback)) {
// Reserve one minutes worth of complete samples (assuming 10ms buffers) to
// hopefully avoid allocating them on the realtime thread.
complete_samples_.reserve(6);
}
InputGlitchCounter::~InputGlitchCounter() {
// Subtract 'trailing' error counts that will happen if the renderer process
// was killed or e.g. the page refreshed while the input device was open etc.
// The counts should then only include data from before the teardown period.
DCHECK_GE(write_count_, global_sample_.trailing_missed_read_deadline_count_);
DCHECK_GE(global_sample_.missed_read_deadline_count_,
global_sample_.trailing_missed_read_deadline_count_);
DCHECK_GE(global_sample_.dropped_data_count_,
global_sample_.trailing_dropped_data_count_);
write_count_ -= global_sample_.trailing_missed_read_deadline_count_;
global_sample_.missed_read_deadline_count_ -=
global_sample_.trailing_missed_read_deadline_count_;
global_sample_.dropped_data_count_ -=
global_sample_.trailing_dropped_data_count_;
// Log whether or not there was any dropped data at all (before the teardown
// period).
base::UmaHistogramEnumeration("Media.AudioCapturerAudioGlitches",
global_sample_.dropped_data_count_ == 0
? AudioGlitchResult::kNoGlitches
: AudioGlitchResult::kGlitches);
std::string log_string = base::StringPrintf(
"AISW: number of detected audio glitches: %" PRIuS " out of %" PRIuS,
global_sample_.dropped_data_count_, write_count_);
log_callback_.Run(log_string);
if (write_count_ < kSampleInterval) {
// The stream without trailing glitches is shorter than kSampleInterval
// callbacks, which means UploadCompleteSamples() has never uploaded
// anything. Adding stats to dedicated histograms.
base::UmaHistogramCounts1000("Media.AudioCapturerDroppedDataBelow10s",
global_sample_.dropped_data_count_);
base::UmaHistogramCounts1000(
"Media.AudioCapturerMissedReadDeadlineBelow10s",
global_sample_.missed_read_deadline_count_);
}
// All remaining not uploaded |complete_samples_| will be dropped, since the
// first of them at least partially and the rest fully consist of trailing
// glitches.
}
void InputGlitchCounter::ReportDroppedData(bool dropped_data) {
if (dropped_data) {
++current_sample_.dropped_data_count_;
++global_sample_.dropped_data_count_;
++global_sample_.trailing_dropped_data_count_;
} else {
// Since we have just successfully written data, we can assume that the
// receiver is still alive, so there should not be any trailing errors.
// Thererfore we can upload any complete samples we have stored.
global_sample_.trailing_dropped_data_count_ = 0;
global_sample_.trailing_missed_read_deadline_count_ = 0;
UploadCompleteSamples();
}
}
void InputGlitchCounter::ReportMissedReadDeadline(bool missed_read_deadline) {
++write_count_;
if (missed_read_deadline) {
++current_sample_.missed_read_deadline_count_;
++global_sample_.missed_read_deadline_count_;
++global_sample_.trailing_missed_read_deadline_count_;
}
// If it's the end of the sample window, then it's time queue the current
// sample to be uploaded and start adding data to a new sample.
if (write_count_ % kSampleInterval == 0) {
// The sample will be uploaded once we know that it does not contain
// trailing errors.
complete_samples_.push_back(current_sample_);
current_sample_ = {};
}
}
void InputGlitchCounter::UploadCompleteSamples() {
for (Sample& sample : complete_samples_) {
base::UmaHistogramCounts1000("Media.AudioCapturerDroppedData10sIntervals",
sample.dropped_data_count_);
base::UmaHistogramCounts1000(
"Media.AudioCapturerMissedReadDeadline10sIntervals",
sample.missed_read_deadline_count_);
}
complete_samples_.clear();
}
} // namespace audio