[go: up one dir, main page]

M104 merge: reland: Report capture OS glitch metrics every 1000 callbacks

NOTE: This is not a clean merge! There was a minor merge conflict with expiry dates in histograms.xml. See diff PS1 vs PS2.

Original CL description:

Patchset 1 is identical to the original CL.
The latest patchset contains a minor fix to the metric names.

Original CL: https://crrev.com/c/3707681
Original message:

Before this CL, OS capture glitch metrics are reported per stream lifetime, which gives disproportionate weight to short streams and transient issues on stream start.
After, metrics are reported and reset every 1000 OS callbacks (every 10 seconds when buffers are 10 ms).

Text logs are still reported once per active stream.

This uses the same method of reporting glitch metrics as for render streams in https://crrev.com/c/3695571.

(cherry picked from commit 7f8c2d32f9e7e91148aa5246a2a3ea5deaefc6c4)

Bug: chromium:1336899
Change-Id: I566204ac07044567ee08fb002fb604b7c7648ebd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3720960
Reviewed-by: Evan Liu <evliu@google.com>
Reviewed-by: Olga Sharonova <olka@chromium.org>
Commit-Queue: Sam Zackrisson <saza@chromium.org>
Cr-Original-Commit-Position: refs/heads/main@{#1019144}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3749202
Cr-Commit-Position: refs/branch-heads/5112@{#681}
Cr-Branched-From: b13d3fe7b3c47a56354ef54b221008afa754412e-refs/heads/main@{#1012729}
diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn
index f8b5193b..46faaaea99 100644
--- a/media/audio/BUILD.gn
+++ b/media/audio/BUILD.gn
@@ -137,8 +137,8 @@
     "scoped_task_runner_observer.h",
     "simple_sources.cc",
     "simple_sources.h",
-    "system_output_glitch_reporter.cc",
-    "system_output_glitch_reporter.h",
+    "system_glitch_reporter.cc",
+    "system_glitch_reporter.h",
     "wav_audio_handler.cc",
     "wav_audio_handler.h",
   ]
diff --git a/media/audio/mac/audio_auhal_mac.cc b/media/audio/mac/audio_auhal_mac.cc
index 0bebb61..86fd0d7 100644
--- a/media/audio/mac/audio_auhal_mac.cc
+++ b/media/audio/mac/audio_auhal_mac.cc
@@ -169,6 +169,7 @@
       current_lost_frames_(0),
       last_sample_time_(0.0),
       last_number_of_frames_(0),
+      glitch_reporter_(SystemGlitchReporter::StreamType::kRender),
       log_callback_(log_callback) {
   // We must have a manager.
   DCHECK(manager_);
@@ -438,7 +439,7 @@
   UMA_HISTOGRAM_COUNTS_1M("Media.Audio.Render.FramesRequested",
                           number_of_frames_requested_);
 
-  SystemOutputGlitchReporter::Stats stats =
+  SystemGlitchReporter::Stats stats =
       glitch_reporter_.GetLongTermStatsAndReset();
 
   std::string log_message = base::StringPrintf(
diff --git a/media/audio/mac/audio_auhal_mac.h b/media/audio/mac/audio_auhal_mac.h
index 16f7244..32bf0833 100644
--- a/media/audio/mac/audio_auhal_mac.h
+++ b/media/audio/mac/audio_auhal_mac.h
@@ -33,7 +33,7 @@
 #include "media/audio/audio_io.h"
 #include "media/audio/audio_manager.h"
 #include "media/audio/mac/scoped_audio_unit.h"
-#include "media/audio/system_output_glitch_reporter.h"
+#include "media/audio/system_glitch_reporter.h"
 #include "media/base/audio_parameters.h"
 
 namespace media {
@@ -204,7 +204,7 @@
 
   // Used to aggregate and report glitch metrics to UMA (periodically) and to
   // text logs (when a stream ends).
-  SystemOutputGlitchReporter glitch_reporter_ GUARDED_BY(lock_);
+  SystemGlitchReporter glitch_reporter_ GUARDED_BY(lock_);
 
   // Used to defer Start() to workaround http://crbug.com/160920.
   base::CancelableOnceClosure deferred_start_cb_;
diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc
index 0e842caf..de981fa 100644
--- a/media/audio/mac/audio_low_latency_input_mac.cc
+++ b/media/audio/mac/audio_low_latency_input_mac.cc
@@ -228,9 +228,7 @@
       output_device_id_for_aec_(kAudioObjectUnknown),
       last_sample_time_(0.0),
       last_number_of_frames_(0),
-      total_lost_frames_(0),
-      largest_glitch_frames_(0),
-      glitches_detected_(0),
+      glitch_reporter_(SystemGlitchReporter::StreamType::kCapture),
       log_callback_(log_callback) {
   DCHECK(manager_);
   CHECK(log_callback_ != AudioManager::LogCallback());
@@ -1391,18 +1389,13 @@
 
   if (last_sample_time_) {
     DCHECK_NE(0U, last_number_of_frames_);
-    UInt32 diff =
+    UInt32 sample_time_diff =
         static_cast<UInt32>(timestamp->mSampleTime - last_sample_time_);
-    if (diff != last_number_of_frames_) {
-      DCHECK_GT(diff, last_number_of_frames_);
-      // We were given samples post what we expected. Update the glitch count
-      // etc. and keep a record of the largest glitch.
-      auto lost_frames = diff - last_number_of_frames_;
-      total_lost_frames_ += lost_frames;
-      if (lost_frames > largest_glitch_frames_)
-        largest_glitch_frames_ = lost_frames;
-      ++glitches_detected_;
-    }
+    DCHECK_GE(sample_time_diff, last_number_of_frames_);
+    UInt32 lost_frames = sample_time_diff - last_number_of_frames_;
+    base::TimeDelta lost_audio_duration = AudioTimestampHelper::FramesToTime(
+        lost_frames, input_params_.sample_rate());
+    glitch_reporter_.UpdateStats(lost_audio_duration);
   }
 
   // Store the last sample time for use next time we get called back.
@@ -1416,33 +1409,24 @@
   // A value of 0 indicates that we got the buffer size we asked for.
   UMA_HISTOGRAM_COUNTS_10000("Media.Audio.Capture.FramesProvided",
                              number_of_frames_provided_);
-  // Even if there aren't any glitches, we want to record it to get a feel for
-  // how often we get no glitches vs the alternative.
-  UMA_HISTOGRAM_COUNTS_1M("Media.Audio.Capture.Glitches", glitches_detected_);
 
-  auto lost_frames_ms = (total_lost_frames_ * 1000) / format_.mSampleRate;
+  SystemGlitchReporter::Stats stats =
+      glitch_reporter_.GetLongTermStatsAndReset();
+
   std::string log_message = base::StringPrintf(
-      "AU in: Total glitches=%d. Total frames lost=%d (%.0lf ms).",
-      glitches_detected_, total_lost_frames_, lost_frames_ms);
-  log_callback_.Run(log_message);
+      "AU in: (num_glitches_detected=[%d], cumulative_audio_lost=[%llu ms], "
+      "largest_glitch=[%llu ms])",
+      stats.glitches_detected, stats.total_glitch_duration.InMilliseconds(),
+      stats.largest_glitch_duration.InMilliseconds());
 
-  if (glitches_detected_ != 0) {
-    UMA_HISTOGRAM_LONG_TIMES("Media.Audio.Capture.LostFramesInMs",
-                             base::Milliseconds(lost_frames_ms));
-    auto largest_glitch_ms =
-        (largest_glitch_frames_ * 1000) / format_.mSampleRate;
-    UMA_HISTOGRAM_CUSTOM_TIMES("Media.Audio.Capture.LargestGlitchMs",
-                               base::Milliseconds(largest_glitch_ms),
-                               base::Milliseconds(1), base::Minutes(1), 50);
+  log_callback_.Run(log_message);
+  if (stats.glitches_detected != 0) {
     DLOG(WARNING) << log_message;
   }
 
   number_of_frames_provided_ = 0;
-  glitches_detected_ = 0;
   last_sample_time_ = 0;
   last_number_of_frames_ = 0;
-  total_lost_frames_ = 0;
-  largest_glitch_frames_ = 0;
 }
 
 // TODO(ossu): Ideally, we'd just use the mono stream directly. However, since
diff --git a/media/audio/mac/audio_low_latency_input_mac.h b/media/audio/mac/audio_low_latency_input_mac.h
index 9e5b3a2..4002c9b 100644
--- a/media/audio/mac/audio_low_latency_input_mac.h
+++ b/media/audio/mac/audio_low_latency_input_mac.h
@@ -50,6 +50,7 @@
 #include "media/audio/agc_audio_stream.h"
 #include "media/audio/audio_io.h"
 #include "media/audio/mac/audio_manager_mac.h"
+#include "media/audio/system_glitch_reporter.h"
 #include "media/base/audio_block_fifo.h"
 #include "media/base/audio_parameters.h"
 
@@ -268,9 +269,10 @@
   // NOTE: Float64 and UInt32 types are used for native API compatibility.
   Float64 last_sample_time_;
   UInt32 last_number_of_frames_;
-  UInt32 total_lost_frames_;
-  UInt32 largest_glitch_frames_;
-  int glitches_detected_;
+
+  // Used to aggregate and report glitch metrics to UMA (periodically) and to
+  // text logs (when a stream ends).
+  SystemGlitchReporter glitch_reporter_;
 
   // Callback to send statistics info.
   AudioManager::LogCallback log_callback_;
diff --git a/media/audio/system_output_glitch_reporter.cc b/media/audio/system_glitch_reporter.cc
similarity index 60%
rename from media/audio/system_output_glitch_reporter.cc
rename to media/audio/system_glitch_reporter.cc
index 5a11dd5..ce06567 100644
--- a/media/audio/system_output_glitch_reporter.cc
+++ b/media/audio/system_glitch_reporter.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "media/audio/system_output_glitch_reporter.h"
+#include "media/audio/system_glitch_reporter.h"
 
 #include "base/metrics/histogram_functions.h"
 
@@ -13,8 +13,20 @@
 constexpr static int kCallbacksPerLogPeriod = 1000;
 }  // namespace
 
-SystemOutputGlitchReporter::Stats
-SystemOutputGlitchReporter::GetLongTermStatsAndReset() {
+SystemGlitchReporter::SystemGlitchReporter(StreamType stream_type)
+    : num_glitches_detected_metric_name_(stream_type == StreamType::kCapture
+                                             ? "Media.Audio.Capture.Glitches2"
+                                             : "Media.Audio.Render.Glitches2"),
+      total_glitch_duration_metric_name_(
+          stream_type == StreamType::kCapture
+              ? "Media.Audio.Capture.LostFramesInMs2"
+              : "Media.Audio.Render.LostFramesInMs2"),
+      largest_glitch_duration_metric_name_(
+          stream_type == StreamType::kCapture
+              ? "Media.Audio.Capture.LargestGlitchMs2"
+              : "Media.Audio.Render.LargestGlitchMs2") {}
+
+SystemGlitchReporter::Stats SystemGlitchReporter::GetLongTermStatsAndReset() {
   Stats result = long_term_stats_;
   callback_count_ = 0;
   short_term_stats_ = {};
@@ -22,7 +34,7 @@
   return result;
 }
 
-void SystemOutputGlitchReporter::UpdateStats(base::TimeDelta glitch_duration) {
+void SystemGlitchReporter::UpdateStats(base::TimeDelta glitch_duration) {
   ++callback_count_;
 
   if (glitch_duration.is_positive()) {
@@ -43,15 +55,15 @@
 
   // We record the glitch count even if there aren't any glitches, to get a
   // feel for how often we get no glitches vs the alternative.
-  base::UmaHistogramCounts1000("Media.Audio.Render.Glitches2",
+  base::UmaHistogramCounts1000(num_glitches_detected_metric_name_,
                                short_term_stats_.glitches_detected);
 
   if (short_term_stats_.glitches_detected != 0) {
     base::UmaHistogramCounts1M(
-        "Media.Audio.Render.LostFramesInMs2",
+        total_glitch_duration_metric_name_,
         short_term_stats_.total_glitch_duration.InMilliseconds());
     base::UmaHistogramCounts1M(
-        "Media.Audio.Render.LargestGlitchMs2",
+        largest_glitch_duration_metric_name_,
         short_term_stats_.largest_glitch_duration.InMilliseconds());
   }
 
diff --git a/media/audio/system_output_glitch_reporter.h b/media/audio/system_glitch_reporter.h
similarity index 69%
rename from media/audio/system_output_glitch_reporter.h
rename to media/audio/system_glitch_reporter.h
index a66e785..8d47109 100644
--- a/media/audio/system_output_glitch_reporter.h
+++ b/media/audio/system_glitch_reporter.h
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef MEDIA_AUDIO_SYSTEM_OUTPUT_GLITCH_REPORTER_H_
-#define MEDIA_AUDIO_SYSTEM_OUTPUT_GLITCH_REPORTER_H_
+#ifndef MEDIA_AUDIO_SYSTEM_GLITCH_REPORTER_H_
+#define MEDIA_AUDIO_SYSTEM_GLITCH_REPORTER_H_
+
+#include <string>
 
 #include "base/time/time.h"
 
@@ -13,15 +15,18 @@
 // Stats are aggregated and reported to UMA periodically every 1000th call to
 // UpdateStats(), and longer-term (manually reset) stats are available via
 // GetLongTermStatsAndReset().
-class SystemOutputGlitchReporter {
+class SystemGlitchReporter {
  public:
+  // Used to determine which UMA metrics to log.
+  enum class StreamType { kCapture, kRender };
+
   struct Stats {
     int glitches_detected = 0;
     base::TimeDelta total_glitch_duration;
     base::TimeDelta largest_glitch_duration;
   };
 
-  SystemOutputGlitchReporter() {}
+  SystemGlitchReporter(StreamType stream_type);
 
   // Resets all state: both periodic and long-term stats.
   Stats GetLongTermStatsAndReset();
@@ -31,6 +36,10 @@
   void UpdateStats(base::TimeDelta glitch_duration);
 
  private:
+  const std::string num_glitches_detected_metric_name_;
+  const std::string total_glitch_duration_metric_name_;
+  const std::string largest_glitch_duration_metric_name_;
+
   int callback_count_ = 0;
 
   // Stats reported periodically to UMA. Resets every 1000 callbacks and on
@@ -43,4 +52,4 @@
 
 }  // namespace media
 
-#endif  // MEDIA_AUDIO_SYSTEM_OUTPUT_GLITCH_REPORTER_H_
+#endif  // MEDIA_AUDIO_SYSTEM_GLITCH_REPORTER_H_
diff --git a/media/audio/win/audio_low_latency_input_win.cc b/media/audio/win/audio_low_latency_input_win.cc
index 8390c811..245a63c 100644
--- a/media/audio/win/audio_low_latency_input_win.cc
+++ b/media/audio/win/audio_low_latency_input_win.cc
@@ -251,12 +251,57 @@
 
 }  // namespace
 
+// Counts how often an OS capture callback reports a data discontinuity and logs
+// it as a UMA histogram.
+class WASAPIAudioInputStream::DataDiscontinuityReporter {
+ public:
+  // Logs once every 10s, assuming 10ms buffers.
+  constexpr static int kCallbacksPerLogPeriod = 1000;
+
+  DataDiscontinuityReporter() {}
+
+  int GetLongTermDiscontinuityCountAndReset() {
+    int long_term_count = data_discontinuity_long_term_count_;
+    callback_count_ = 0;
+    data_discontinuity_short_term_count_ = 0;
+    data_discontinuity_long_term_count_ = 0;
+    return long_term_count;
+  }
+
+  void Log(bool observed_data_discontinuity) {
+    ++callback_count_;
+    if (observed_data_discontinuity) {
+      ++data_discontinuity_short_term_count_;
+      ++data_discontinuity_long_term_count_;
+    }
+
+    if (callback_count_ % kCallbacksPerLogPeriod)
+      return;
+
+    // TODO(https://crbug.com/825744): It can be possible to replace
+    // "Media.Audio.Capture.Glitches2" with this new (simplified) metric
+    // instead.
+    base::UmaHistogramCounts1000("Media.Audio.Capture.Win.Glitches2",
+                                 data_discontinuity_short_term_count_);
+
+    data_discontinuity_short_term_count_ = 0;
+  }
+
+ private:
+  int callback_count_ = 0;
+  int data_discontinuity_short_term_count_ = 0;
+  int data_discontinuity_long_term_count_ = 0;
+};
+
 WASAPIAudioInputStream::WASAPIAudioInputStream(
     AudioManagerWin* manager,
     const AudioParameters& params,
     const std::string& device_id,
     AudioManager::LogCallback log_callback)
     : manager_(manager),
+      glitch_reporter_(SystemGlitchReporter::StreamType::kCapture),
+      data_discontinuity_reporter_(
+          std::make_unique<DataDiscontinuityReporter>()),
       device_id_(device_id),
       log_callback_(std::move(log_callback)) {
   DCHECK(manager_);
@@ -846,16 +891,17 @@
     // The data in the packet is not correlated with the previous packet's
     // device position; this is possibly due to a stream state transition or
     // timing glitch. Note that, usage of this flag was added after the existing
-    // glitch detection in UpdateGlitchCount() and it will be used as a
-    // supplementary scheme initially.
+    // glitch detection and it will be used as a supplementary scheme initially.
     // The behavior of the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY flag is
     // undefined on the application's first call to GetBuffer after Start and
     // Windows 7 or later is required for support.
-    if (device_position > 0 && flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) {
+    const bool observed_data_discontinuity =
+        (device_position > 0 && flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY);
+    if (observed_data_discontinuity) {
       LOG(WARNING) << "WAIS::" << __func__
                    << " => (WARNING: AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY)";
-      ++num_data_discontinuity_warnings_;
     }
+    data_discontinuity_reporter_->Log(observed_data_discontinuity);
 
     // The time at which the device's stream position was recorded is uncertain.
     // Thus, the client might be unable to accurately set a time stamp for the
@@ -881,7 +927,16 @@
     // If the device position has not changed we assume this data belongs to the
     // previous chunk, and only update the expected next device position.
     if (device_position != last_device_position) {
-      UpdateGlitchCount(device_position);
+      if (expected_next_device_position_ != 0) {
+        base::TimeDelta glitch_duration;
+        if (device_position > expected_next_device_position_) {
+          glitch_duration = AudioTimestampHelper::FramesToTime(
+              device_position - expected_next_device_position_,
+              input_format_.Format.nSamplesPerSec);
+        }
+        glitch_reporter_.UpdateStats(glitch_duration);
+      }
+
       last_device_position = device_position;
       expected_next_device_position_ = device_position + num_frames_to_read;
     } else {
@@ -1598,10 +1653,10 @@
           ? converter_.get()
                 ? FormatRelatedInitError::kUnsupportedFormatWithFormatConversion
                 : FormatRelatedInitError::kUnsupportedFormat
-          // Otherwise |hr| == E_INVALIDARG.
-          : converter_.get()
-                ? FormatRelatedInitError::kInvalidArgumentWithFormatConversion
-                : FormatRelatedInitError::kInvalidArgument;
+      // Otherwise |hr| == E_INVALIDARG.
+      : converter_.get()
+          ? FormatRelatedInitError::kInvalidArgumentWithFormatConversion
+          : FormatRelatedInitError::kInvalidArgument;
   base::UmaHistogramEnumeration(
       "Media.Audio.Capture.Win.InitError.FormatRelated", format_related_error,
       FormatRelatedInitError::kCount);
@@ -1613,41 +1668,20 @@
   return 1.0;
 }
 
-void WASAPIAudioInputStream::UpdateGlitchCount(UINT64 device_position) {
-  if (expected_next_device_position_ != 0) {
-    if (device_position > expected_next_device_position_) {
-      ++total_glitches_;
-      auto lost_frames = device_position - expected_next_device_position_;
-      total_lost_frames_ += lost_frames;
-      if (lost_frames > largest_glitch_frames_)
-        largest_glitch_frames_ = lost_frames;
-    }
-  }
-}
-
 void WASAPIAudioInputStream::ReportAndResetGlitchStats() {
-  UMA_HISTOGRAM_COUNTS_1M("Media.Audio.Capture.Glitches", total_glitches_);
-  double lost_frames_ms =
-      (total_lost_frames_ * 1000) / input_format_.Format.nSamplesPerSec;
+  SystemGlitchReporter::Stats stats =
+      glitch_reporter_.GetLongTermStatsAndReset();
   SendLogMessage(
-      "%s => (total glitches=[%d], total frames lost=[%llu/%.0lf ms])",
-      __func__, total_glitches_, total_lost_frames_, lost_frames_ms);
-  if (total_glitches_ != 0) {
-    UMA_HISTOGRAM_LONG_TIMES("Media.Audio.Capture.LostFramesInMs",
-                             base::Milliseconds(lost_frames_ms));
-    int64_t largest_glitch_ms =
-        (largest_glitch_frames_ * 1000) / input_format_.Format.nSamplesPerSec;
-    UMA_HISTOGRAM_CUSTOM_TIMES("Media.Audio.Capture.LargestGlitchMs",
-                               base::Milliseconds(largest_glitch_ms),
-                               base::Milliseconds(1), base::Minutes(1), 50);
-  }
+      "%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());
 
-  // TODO(https://crbug.com/825744): It can be possible to replace
-  // "Media.Audio.Capture.Glitches" with this new (simplified) metric instead.
-  base::UmaHistogramCounts1M("Media.Audio.Capture.Win.Glitches",
-                             num_data_discontinuity_warnings_);
-  SendLogMessage("%s => (discontinuity warnings=[%" PRIu64 "])", __func__,
-                 num_data_discontinuity_warnings_);
+  int num_data_discontinuities =
+      data_discontinuity_reporter_->GetLongTermDiscontinuityCountAndReset();
+  SendLogMessage("%s => (discontinuity warnings=[%d])", __func__,
+                 num_data_discontinuities);
   SendLogMessage("%s => (timstamp errors=[%" PRIu64 "])", __func__,
                  num_timestamp_errors_);
   if (num_timestamp_errors_ > 0) {
@@ -1657,10 +1691,6 @@
   }
 
   expected_next_device_position_ = 0;
-  total_glitches_ = 0;
-  total_lost_frames_ = 0;
-  largest_glitch_frames_ = 0;
-  num_data_discontinuity_warnings_ = 0;
   num_timestamp_errors_ = 0;
 }
 
diff --git a/media/audio/win/audio_low_latency_input_win.h b/media/audio/win/audio_low_latency_input_win.h
index 05f5f87..caef8a2 100644
--- a/media/audio/win/audio_low_latency_input_win.h
+++ b/media/audio/win/audio_low_latency_input_win.h
@@ -78,6 +78,7 @@
 #include "base/win/scoped_com_initializer.h"
 #include "base/win/scoped_handle.h"
 #include "media/audio/agc_audio_stream.h"
+#include "media/audio/system_glitch_reporter.h"
 #include "media/audio/win/audio_manager_win.h"
 #include "media/base/audio_converter.h"
 #include "media/base/audio_parameters.h"
@@ -147,6 +148,8 @@
   bool started() const { return started_; }
 
  private:
+  class DataDiscontinuityReporter;
+
   void SendLogMessage(const char* format, ...) PRINTF_FORMAT(2, 3);
 
   // DelegateSimpleThread::Delegate implementation.
@@ -199,15 +202,20 @@
   // AudioConverter::InputCallback implementation.
   double ProvideInput(AudioBus* audio_bus, uint32_t frames_delayed) override;
 
-  // Detects and counts glitches based on |device_position|.
-  void UpdateGlitchCount(UINT64 device_position);
-
   // Reports glitch stats and resets associated variables.
   void ReportAndResetGlitchStats();
 
   // Our creator, the audio manager needs to be notified when we close.
   const raw_ptr<AudioManagerWin> manager_;
 
+  // Used to aggregate and report glitch metrics to UMA (periodically) and to
+  // text logs (when a stream ends).
+  SystemGlitchReporter glitch_reporter_;
+
+  // Used to track and log data discontinuity warnings from
+  // IAudioCaptureClient::GetBuffer.
+  std::unique_ptr<DataDiscontinuityReporter> data_discontinuity_reporter_;
+
   // Capturing is driven by this thread (which has no message loop).
   // All OnData() callbacks will be called from this thread.
   std::unique_ptr<base::DelegateSimpleThread> capture_thread_;
@@ -313,12 +321,8 @@
 
   // For detecting and reporting glitches.
   UINT64 expected_next_device_position_ = 0;
-  int total_glitches_ = 0;
-  UINT64 total_lost_frames_ = 0;
-  UINT64 largest_glitch_frames_ = 0;
 
   // Tracks error messages from IAudioCaptureClient::GetBuffer.
-  UINT64 num_data_discontinuity_warnings_ = 0;
   UINT64 num_timestamp_errors_ = 0;
   base::TimeTicks record_start_time_;
   base::TimeDelta time_until_first_timestamp_error_;
diff --git a/media/audio/win/audio_low_latency_output_win.cc b/media/audio/win/audio_low_latency_output_win.cc
index c52c92f..5e22cf8 100644
--- a/media/audio/win/audio_low_latency_output_win.cc
+++ b/media/audio/win/audio_low_latency_output_win.cc
@@ -87,6 +87,7 @@
     AudioManager::LogCallback log_callback)
     : creating_thread_id_(base::PlatformThread::CurrentId()),
       manager_(manager),
+      glitch_reporter_(SystemGlitchReporter::StreamType::kRender),
       format_(),
       opened_(false),
       volume_(1.0),
@@ -490,8 +491,8 @@
   // Enable MMCSS to ensure that this thread receives prioritized access to
   // CPU resources.
   DWORD task_index = 0;
-  HANDLE mm_task = avrt::AvSetMmThreadCharacteristics(L"Pro Audio",
-                                                      &task_index);
+  HANDLE mm_task =
+      avrt::AvSetMmThreadCharacteristics(L"Pro Audio", &task_index);
   bool mmcss_is_ok =
       (mm_task && avrt::AvSetMmThreadPriority(mm_task, AVRT_PRIORITY_CRITICAL));
   if (!mmcss_is_ok) {
@@ -507,8 +508,8 @@
 
   bool playing = true;
   bool error = false;
-  HANDLE wait_array[] = { stop_render_event_.Get(),
-                          audio_samples_render_event_.Get() };
+  HANDLE wait_array[] = {stop_render_event_.Get(),
+                         audio_samples_render_event_.Get()};
   UINT64 device_frequency = 0;
 
   // The device frequency is the frequency generated by the hardware clock in
@@ -582,8 +583,7 @@
     // Get the padding value which represents the amount of rendering
     // data that is queued up to play in the endpoint buffer.
     hr = audio_client_->GetCurrentPadding(&num_queued_frames);
-    num_available_frames =
-        endpoint_buffer_size_frames_ - num_queued_frames;
+    num_available_frames = endpoint_buffer_size_frames_ - num_queued_frames;
     if (FAILED(hr)) {
       RecordAudioFailure(kRenderFailureHistogram, hr);
       LOG(ERROR) << "WAOS::" << __func__
@@ -628,8 +628,7 @@
   for (size_t n = 0; n < num_packets; ++n) {
     // Grab all available space in the rendering endpoint buffer
     // into which the client can write a data packet.
-    hr = audio_render_client_->GetBuffer(packet_size_frames_,
-                                         &audio_data);
+    hr = audio_render_client_->GetBuffer(packet_size_frames_, &audio_data);
     if (FAILED(hr)) {
       RecordAudioFailure(kRenderFailureHistogram, hr);
       LOG(ERROR) << "WAOS::" << __func__
@@ -721,8 +720,9 @@
 
     // Release the buffer space acquired in the GetBuffer() call.
     // Render silence if we were not able to fill up the buffer totally.
-    DWORD flags = (num_filled_bytes < packet_size_bytes_) ?
-        AUDCLNT_BUFFERFLAGS_SILENT : 0;
+    DWORD flags = (num_filled_bytes < packet_size_bytes_)
+                      ? AUDCLNT_BUFFERFLAGS_SILENT
+                      : 0;
     audio_render_client_->ReleaseBuffer(packet_size_frames_, flags);
 
     num_written_frames_ += packet_size_frames_;
@@ -771,8 +771,8 @@
       // Calculate new aligned periodicity. Each unit of reference time
       // is 100 nanoseconds.
       REFERENCE_TIME aligned_buffer_duration = static_cast<REFERENCE_TIME>(
-          (10000000.0 * aligned_buffer_size / format_.Format.nSamplesPerSec)
-          + 0.5);
+          (10000000.0 * aligned_buffer_size / format_.Format.nSamplesPerSec) +
+          0.5);
 
       // It is possible to re-activate and re-initialize the audio client
       // at this stage but we bail out with an error code instead and
@@ -828,7 +828,7 @@
 }
 
 void WASAPIAudioOutputStream::ReportAndResetStats() {
-  SystemOutputGlitchReporter::Stats stats =
+  SystemGlitchReporter::Stats stats =
       glitch_reporter_.GetLongTermStatsAndReset();
   SendLogMessage(
       "%s => (num_glitches_detected=[%d], cumulative_audio_lost=[%llu ms], "
diff --git a/media/audio/win/audio_low_latency_output_win.h b/media/audio/win/audio_low_latency_output_win.h
index 96058452..27c9b1ea 100644
--- a/media/audio/win/audio_low_latency_output_win.h
+++ b/media/audio/win/audio_low_latency_output_win.h
@@ -112,7 +112,7 @@
 #include "base/win/scoped_com_initializer.h"
 #include "base/win/scoped_handle.h"
 #include "media/audio/audio_io.h"
-#include "media/audio/system_output_glitch_reporter.h"
+#include "media/audio/system_glitch_reporter.h"
 #include "media/audio/win/audio_manager_win.h"
 #include "media/base/audio_parameters.h"
 #include "media/base/media_export.h"
@@ -196,7 +196,7 @@
 
   // Used to aggregate and report glitch metrics to UMA (periodically) and to
   // text logs (when a stream ends).
-  SystemOutputGlitchReporter glitch_reporter_;
+  SystemGlitchReporter glitch_reporter_;
 
   // Rendering is driven by this thread (which has no message loop).
   // All OnMoreData() callbacks will be called from this thread.
diff --git a/tools/metrics/histograms/metadata/media/histograms.xml b/tools/metrics/histograms/metadata/media/histograms.xml
index 816d2f7..4920c35 100644
--- a/tools/metrics/histograms/metadata/media/histograms.xml
+++ b/tools/metrics/histograms/metadata/media/histograms.xml
@@ -234,7 +234,10 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.Glitches" units="glitches"
-    expires_after="2022-10-16">
+    expires_after="2022-12-18">
+  <obsolete>
+    Removed 06/2022, replaced by Media.Audio.Capture.Glitches2.
+  </obsolete>
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -266,7 +269,10 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.LargestGlitchMs" units="ms"
-    expires_after="2022-10-16">
+    expires_after="2022-12-18">
+  <obsolete>
+    Removed 06/2022, replaced by Media.Audio.Capture.LargestGlitchMs2.
+  </obsolete>
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -277,7 +283,10 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.LostFramesInMs" units="ms"
-    expires_after="2022-10-23">
+    expires_after="2022-12-25">
+  <obsolete>
+    Removed 06/2022, replaced by Media.Audio.Capture.LostFramesInMs2.
+  </obsolete>
   <owner>guidou@chromium.org</owner>
   <owner>olka@chromium.org</owner>
   <summary>
@@ -421,7 +430,10 @@
 </histogram>
 
 <histogram name="Media.Audio.Capture.Win.Glitches" units="glitches"
-    expires_after="2022-10-08">
+    expires_after="2022-12-11">
+  <obsolete>
+    Removed 06/2022, replaced by Media.Audio.Render.Win.Glitches2.
+  </obsolete>
   <owner>henrika@chromium.org</owner>
   <owner>media-dev@chromium.org</owner>
   <summary>
@@ -433,6 +445,19 @@
   </summary>
 </histogram>
 
+<histogram name="Media.Audio.Capture.Win.Glitches2" units="glitches"
+    expires_after="2022-12-11">
+  <owner>henrika@chromium.org</owner>
+  <owner>media-dev@chromium.org</owner>
+  <summary>
+    The number of glitches that were detected at the OS level while an audio
+    input stream was active. A glitch in this case corresponds to a certain
+    error code from the IAudioCaptureClient::GetBuffer() API:
+    AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY. This value is logged every 1000 OS
+    callbacks on Windows.
+  </summary>
+</histogram>
+
 <histogram name="Media.Audio.Capture.Win.InitError" enum="Hresult"
     expires_after="2022-10-30">
   <owner>tommi@chromium.org</owner>
@@ -990,20 +1015,6 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.Render.Glitches2" units="glitches"
-    expires_after="2023-06-10">
-  <owner>olka@chromium.org</owner>
-  <owner>saza@chromium.org</owner>
-  <summary>
-    The number of glitches that were detected at the OS level while an audio
-    output stream was active. A glitch is detected if the OS has to skip
-    rendering a set of frames. This value is aggregated over a period of 1000 OS
-    callbacks (every 10 seconds for buffer size 10 ms) and is logged at the end
-    of each collection interval. Not recorded for streams shorter than 1000
-    callbacks. Reported on Windows and Mac.
-  </summary>
-</histogram>
-
 <histogram name="Media.Audio.Render.LargestGlitchMs" units="ms"
     expires_after="2022-10-16">
   <obsolete>
@@ -1018,20 +1029,6 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.Render.LargestGlitchMs2" units="ms"
-    expires_after="2023-06-10">
-  <owner>olka@chromium.org</owner>
-  <owner>saza@chromium.org</owner>
-  <summary>
-    The length in milliseconds of the largest glitch that was detected at the OS
-    level by an audio output stream. This value is aggregated over a period of
-    1000 OS callbacks (every 10 seconds for buffer size 10 ms) and is logged at
-    the end of each collection interval if any glitches were detected. Not
-    recorded for streams shorter than 1000 callbacks. Reported on Windows and
-    Mac.
-  </summary>
-</histogram>
-
 <histogram name="Media.Audio.Render.LostFramesInMs" units="ms"
     expires_after="2022-10-23">
   <obsolete>
@@ -1047,20 +1044,6 @@
   </summary>
 </histogram>
 
-<histogram name="Media.Audio.Render.LostFramesInMs2" units="ms"
-    expires_after="2023-06-10">
-  <owner>olka@chromium.org</owner>
-  <owner>saza@chromium.org</owner>
-  <summary>
-    The combined length in milliseconds of audio glitches. This is useful to
-    know in particular for audio processing such as echo cancellation. This
-    value is aggregated over a period of 1000 OS callbacks (every 10 seconds for
-    buffer size 10 ms) and is logged at the end of each collection interval if
-    any glitches were detected. Not recorded for streams shorter than 1000
-    callbacks. Reported on Windows and Mac.
-  </summary>
-</histogram>
-
 <histogram name="Media.Audio.Render.OutputDeviceAuthorizationTime" units="ms"
     expires_after="2023-04-04">
   <owner>olka@chromium.org</owner>
@@ -1260,6 +1243,62 @@
   </summary>
 </histogram>
 
+<histogram name="Media.Audio.{Type}.Glitches2" units="glitches"
+    expires_after="2023-06-10">
+  <owner>olka@chromium.org</owner>
+  <owner>saza@chromium.org</owner>
+  <summary>
+    The number of glitches that were detected at the OS level while an audio
+    input (Capture) or output (Render) stream was active. A glitch is detected
+    if the OS skips providing/requesting any frames of audio. This value is
+    aggregated over a period of 1000 OS callbacks (every 10 seconds for buffer
+    size 10 ms) and is logged at the end of each collection interval. Not
+    recorded for streams shorter than 1000 callbacks. Reported on Windows and
+    Mac.
+  </summary>
+  <token key="Type">
+    <variant name="Capture"/>
+    <variant name="Render"/>
+  </token>
+</histogram>
+
+<histogram name="Media.Audio.{Type}.LargestGlitchMs2" units="ms"
+    expires_after="2023-06-10">
+  <owner>olka@chromium.org</owner>
+  <owner>saza@chromium.org</owner>
+  <summary>
+    The length in milliseconds of the largest glitch that was detected at the OS
+    level by an audio input (Capture) or output (Render) stream. This value is
+    aggregated over a period of 1000 OS callbacks (every 10 seconds for buffer
+    size 10 ms) and is logged at the end of each collection interval if any
+    glitches were detected. Not recorded for streams shorter than 1000
+    callbacks. Reported on Windows and Mac.
+  </summary>
+  <token key="Type">
+    <variant name="Capture"/>
+    <variant name="Render"/>
+  </token>
+</histogram>
+
+<histogram name="Media.Audio.{Type}.LostFramesInMs2" units="ms"
+    expires_after="2023-06-10">
+  <owner>olka@chromium.org</owner>
+  <owner>saza@chromium.org</owner>
+  <summary>
+    The combined length in milliseconds of input (Capture) or output (Render)
+    audio glitches detected at the OS level. This is useful to know in
+    particular for audio processing such as echo cancellation. This value is
+    aggregated over a period of 1000 OS callbacks (every 10 seconds for buffer
+    size 10 ms) and is logged at the end of each collection interval if any
+    glitches were detected. Not recorded for streams shorter than 1000
+    callbacks. Reported on Windows and Mac.
+  </summary>
+  <token key="Type">
+    <variant name="Capture"/>
+    <variant name="Render"/>
+  </token>
+</histogram>
+
 <histogram name="Media.AudioBitsPerChannel" units="units" expires_after="M85">
   <owner>dalecurtis@chromium.org</owner>
   <summary>Bits per channel of HTML5 audio sample data.</summary>