audio_processing: Added a new AEC delay metric value that gives the amount of poor delays

To more easily determine if for example the AEC is not working properly one could monitor how often the estimated delay is out of bounds. With out of bounds we mean either being negative or too large, where both cases will break the AEC.

A new delay metric is added telling the user how often poor delay values were estimated. This is measured in percentage since last time the metrics were calculated.

All APIs have been updated with a third parameter with EchoCancellation::GetDelayMetrics() giving the option to exclude the new metric not to break existing code.

The new metric has been added to audio_processing_unittests with an additional protobuf member, and reference files accordingly updated.
voe_auto_test has not been updated to display the new metric.

BUG=4246
TESTED=audioproc on files
R=aluebs@webrtc.org, andrew@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/39739004

Cr-Commit-Position: refs/heads/master@{#8230}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8230 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/data/audio_processing/output_data_float.pb b/data/audio_processing/output_data_float.pb
index 13805c0..03f757e 100644
--- a/data/audio_processing/output_data_float.pb
+++ b/data/audio_processing/output_data_float.pb
Binary files differ
diff --git a/data/audio_processing/output_data_mac.pb b/data/audio_processing/output_data_mac.pb
index a74778f..47c67f4 100644
--- a/data/audio_processing/output_data_mac.pb
+++ b/data/audio_processing/output_data_mac.pb
Binary files differ
diff --git a/webrtc/modules/audio_processing/aec/aec_core.c b/webrtc/modules/audio_processing/aec/aec_core.c
index 1f864fc..2cd7d85 100644
--- a/webrtc/modules/audio_processing/aec/aec_core.c
+++ b/webrtc/modules/audio_processing/aec/aec_core.c
@@ -774,6 +774,7 @@
   int i = 0;
   int delay_values = 0;
   int median = 0;
+  int lookahead = WebRtc_lookahead(self->delay_estimator);
   const int kMsPerBlock = PART_LEN / (self->mult * 8);
   int64_t l1_norm = 0;
 
@@ -785,6 +786,7 @@
     // not able to estimate the delay.
     self->delay_median = -1;
     self->delay_std = -1;
+    self->fraction_poor_delays = -1;
     return;
   }
 
@@ -799,8 +801,7 @@
     }
   }
   // Account for lookahead.
-  self->delay_median = (median - WebRtc_lookahead(self->delay_estimator)) *
-      kMsPerBlock;
+  self->delay_median = (median - lookahead) * kMsPerBlock;
 
   // Calculate the L1 norm, with median value as central moment.
   for (i = 0; i < kHistorySizeBlocks; i++) {
@@ -809,6 +810,17 @@
   self->delay_std = (int)((l1_norm + self->num_delay_values / 2) /
       self->num_delay_values) * kMsPerBlock;
 
+  // Determine fraction of delays that are out of bounds, that is, either
+  // negative (anti-causal system) or larger than the AEC filter length.
+  {
+    int num_delays_out_of_bounds = self->num_delay_values;
+    for (i = lookahead; i < lookahead + self->num_partitions; ++i) {
+      num_delays_out_of_bounds -= self->delay_histogram[i];
+    }
+    self->fraction_poor_delays = (float)num_delays_out_of_bounds /
+        self->num_delay_values;
+  }
+
   // Reset histogram.
   memset(self->delay_histogram, 0, sizeof(self->delay_histogram));
   self->num_delay_values = 0;
@@ -1563,6 +1575,7 @@
   aec->num_delay_values = 0;
   aec->delay_median = -1;
   aec->delay_std = -1;
+  aec->fraction_poor_delays = -1;
 
   aec->signal_delay_correction = 0;
   aec->previous_delay = -2;  // (-2): Uninitialized.
@@ -1833,7 +1846,8 @@
   }
 }
 
-int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std) {
+int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std,
+                                  float* fraction_poor_delays) {
   assert(self != NULL);
   assert(median != NULL);
   assert(std != NULL);
@@ -1849,6 +1863,7 @@
   }
   *median = self->delay_median;
   *std = self->delay_std;
+  *fraction_poor_delays = self->fraction_poor_delays;
 
   return 0;
 }
diff --git a/webrtc/modules/audio_processing/aec/aec_core.h b/webrtc/modules/audio_processing/aec/aec_core.h
index 2bffeec..2b614dd 100644
--- a/webrtc/modules/audio_processing/aec/aec_core.h
+++ b/webrtc/modules/audio_processing/aec/aec_core.h
@@ -75,9 +75,14 @@
 // corresponding amount in ms.
 int WebRtcAec_MoveFarReadPtr(AecCore* aec, int elements);
 
-// Calculates the median and standard deviation among the delay estimates
-// collected since the last call to this function.
-int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std);
+// Calculates the median, standard deviation and amount of poor values among the
+// delay estimates aggregated up to the first call to the function. After that
+// first call the metrics are aggregated and updated every second. With poor
+// values we mean values that most likely will cause the AEC to perform poorly.
+// TODO(bjornv): Consider changing tests and tools to handle constant
+// constant aggregation window throughout the session instead.
+int WebRtcAec_GetDelayMetricsCore(AecCore* self, int* median, int* std,
+                                  float* fraction_poor_delays);
 
 // Returns the echo state (1: echo, 0: no echo).
 int WebRtcAec_echo_state(AecCore* self);
diff --git a/webrtc/modules/audio_processing/aec/aec_core_internal.h b/webrtc/modules/audio_processing/aec/aec_core_internal.h
index b9950f6..bdb9041 100644
--- a/webrtc/modules/audio_processing/aec/aec_core_internal.h
+++ b/webrtc/modules/audio_processing/aec/aec_core_internal.h
@@ -131,6 +131,7 @@
   int num_delay_values;
   int delay_median;
   int delay_std;
+  float fraction_poor_delays;
   int delay_logging_enabled;
   void* delay_estimator_farend;
   void* delay_estimator;
diff --git a/webrtc/modules/audio_processing/aec/echo_cancellation.c b/webrtc/modules/audio_processing/aec/echo_cancellation.c
index 470fe05..016c455 100644
--- a/webrtc/modules/audio_processing/aec/echo_cancellation.c
+++ b/webrtc/modules/audio_processing/aec/echo_cancellation.c
@@ -556,7 +556,10 @@
   return 0;
 }
 
-int WebRtcAec_GetDelayMetrics(void* handle, int* median, int* std) {
+int WebRtcAec_GetDelayMetrics(void* handle,
+                              int* median,
+                              int* std,
+                              float* fraction_poor_delays) {
   Aec* self = handle;
   if (median == NULL) {
     self->lastError = AEC_NULL_POINTER_ERROR;
@@ -570,7 +573,9 @@
     self->lastError = AEC_UNINITIALIZED_ERROR;
     return -1;
   }
-  if (WebRtcAec_GetDelayMetricsCore(self->aec, median, std) == -1) {
+  if (WebRtcAec_GetDelayMetricsCore(self->aec, median, std,
+                                    fraction_poor_delays) ==
+      -1) {
     // Logging disabled.
     self->lastError = AEC_UNSUPPORTED_FUNCTION_ERROR;
     return -1;
diff --git a/webrtc/modules/audio_processing/aec/include/echo_cancellation.h b/webrtc/modules/audio_processing/aec/include/echo_cancellation.h
index 51eb37a..df9b2a9 100644
--- a/webrtc/modules/audio_processing/aec/include/echo_cancellation.h
+++ b/webrtc/modules/audio_processing/aec/include/echo_cancellation.h
@@ -211,17 +211,22 @@
  *
  * Inputs                       Description
  * -------------------------------------------------------------------
- * void*      handle            Pointer to the AEC instance
+ * void*   handle               Pointer to the AEC instance
  *
  * Outputs                      Description
  * -------------------------------------------------------------------
- * int*       median            Delay median value.
- * int*       std               Delay standard deviation.
+ * int*    median               Delay median value.
+ * int*    std                  Delay standard deviation.
+ * float*  fraction_poor_delays Fraction of the delay estimates that may
+ *                              cause the AEC to perform poorly.
  *
- * int        return             0: OK
+ * int     return                0: OK
  *                              -1: error
  */
-int WebRtcAec_GetDelayMetrics(void* handle, int* median, int* std);
+int WebRtcAec_GetDelayMetrics(void* handle,
+                              int* median,
+                              int* std,
+                              float* fraction_poor_delays);
 
 /*
  * Gets the last error code.
diff --git a/webrtc/modules/audio_processing/echo_cancellation_impl.cc b/webrtc/modules/audio_processing/echo_cancellation_impl.cc
index bf5ed72..dd3c7f1 100644
--- a/webrtc/modules/audio_processing/echo_cancellation_impl.cc
+++ b/webrtc/modules/audio_processing/echo_cancellation_impl.cc
@@ -281,6 +281,12 @@
 
 // TODO(bjornv): How should we handle the multi-channel case?
 int EchoCancellationImpl::GetDelayMetrics(int* median, int* std) {
+  float fraction_poor_delays = 0;
+  return GetDelayMetrics(median, std, &fraction_poor_delays);
+}
+
+int EchoCancellationImpl::GetDelayMetrics(int* median, int* std,
+                                          float* fraction_poor_delays) {
   CriticalSectionScoped crit_scoped(crit_);
   if (median == NULL) {
     return apm_->kNullPointerError;
@@ -294,7 +300,7 @@
   }
 
   Handle* my_handle = static_cast<Handle*>(handle(0));
-  if (WebRtcAec_GetDelayMetrics(my_handle, median, std) !=
+  if (WebRtcAec_GetDelayMetrics(my_handle, median, std, fraction_poor_delays) !=
       apm_->kNoError) {
     return GetHandleError(my_handle);
   }
diff --git a/webrtc/modules/audio_processing/echo_cancellation_impl.h b/webrtc/modules/audio_processing/echo_cancellation_impl.h
index b9c116a..4545938 100644
--- a/webrtc/modules/audio_processing/echo_cancellation_impl.h
+++ b/webrtc/modules/audio_processing/echo_cancellation_impl.h
@@ -52,6 +52,8 @@
   virtual int enable_delay_logging(bool enable) OVERRIDE;
   virtual bool is_delay_logging_enabled() const OVERRIDE;
   virtual int GetDelayMetrics(int* median, int* std) OVERRIDE;
+  virtual int GetDelayMetrics(int* median, int* std,
+                              float* fraction_poor_delays) OVERRIDE;
   virtual struct AecCore* aec_core() const OVERRIDE;
 
   // ProcessingComponent implementation.
diff --git a/webrtc/modules/audio_processing/include/audio_processing.h b/webrtc/modules/audio_processing/include/audio_processing.h
index 6b761e1..3715b34 100644
--- a/webrtc/modules/audio_processing/include/audio_processing.h
+++ b/webrtc/modules/audio_processing/include/audio_processing.h
@@ -487,9 +487,17 @@
   virtual bool is_delay_logging_enabled() const = 0;
 
   // The delay metrics consists of the delay |median| and the delay standard
-  // deviation |std|. The values are averaged over the time period since the
-  // last call to |GetDelayMetrics()|.
+  // deviation |std|. It also consists of the fraction of delay estimates
+  // |fraction_poor_delays| that can make the echo cancellation perform poorly.
+  // The values are aggregated until the first call to |GetDelayMetrics()| and
+  // afterwards aggregated and updated every second.
+  // Note that if there are several clients pulling metrics from
+  // |GetDelayMetrics()| during a session the first call from any of them will
+  // change to one second aggregation window for all.
+  // TODO(bjornv): Deprecated, remove.
   virtual int GetDelayMetrics(int* median, int* std) = 0;
+  virtual int GetDelayMetrics(int* median, int* std,
+                              float* fraction_poor_delays) = 0;
 
   // Returns a pointer to the low level AEC component.  In case of multiple
   // channels, the pointer to the first one is returned.  A NULL pointer is
diff --git a/webrtc/modules/audio_processing/include/mock_audio_processing.h b/webrtc/modules/audio_processing/include/mock_audio_processing.h
index 8258bb6..46a04ae 100644
--- a/webrtc/modules/audio_processing/include/mock_audio_processing.h
+++ b/webrtc/modules/audio_processing/include/mock_audio_processing.h
@@ -48,6 +48,8 @@
       bool());
   MOCK_METHOD2(GetDelayMetrics,
       int(int* median, int* std));
+  MOCK_METHOD3(GetDelayMetrics,
+      int(int* median, int* std, float* fraction_poor_delays));
   MOCK_CONST_METHOD0(aec_core,
       struct AecCore*());
 };
diff --git a/webrtc/modules/audio_processing/test/audio_processing_unittest.cc b/webrtc/modules/audio_processing/test/audio_processing_unittest.cc
index 7e36d67..093c72e 100644
--- a/webrtc/modules/audio_processing/test/audio_processing_unittest.cc
+++ b/webrtc/modules/audio_processing/test/audio_processing_unittest.cc
@@ -635,9 +635,11 @@
     if (frame_count == 250) {
       int median;
       int std;
+      float poor_fraction;
       // Discard the first delay metrics to avoid convergence effects.
       EXPECT_EQ(apm_->kNoError,
-                apm_->echo_cancellation()->GetDelayMetrics(&median, &std));
+                apm_->echo_cancellation()->GetDelayMetrics(&median, &std,
+                                                           &poor_fraction));
     }
   }
 
@@ -659,8 +661,10 @@
   // Verify delay metrics.
   int median;
   int std;
+  float poor_fraction;
   EXPECT_EQ(apm_->kNoError,
-            apm_->echo_cancellation()->GetDelayMetrics(&median, &std));
+            apm_->echo_cancellation()->GetDelayMetrics(&median, &std,
+                                                       &poor_fraction));
   EXPECT_GE(expected_median_high, median);
   EXPECT_LE(expected_median_low, median);
 }
@@ -847,8 +851,10 @@
 
   int median = 0;
   int std = 0;
+  float poor_fraction = 0;
   EXPECT_EQ(apm_->kNotEnabledError,
-            apm_->echo_cancellation()->GetDelayMetrics(&median, &std));
+            apm_->echo_cancellation()->GetDelayMetrics(&median, &std,
+                                                       &poor_fraction));
 
   EXPECT_EQ(apm_->kNoError,
             apm_->echo_cancellation()->enable_delay_logging(true));
@@ -2026,8 +2032,10 @@
               apm_->echo_cancellation()->GetMetrics(&echo_metrics));
     int median = 0;
     int std = 0;
+    float fraction_poor_delays = 0;
     EXPECT_EQ(apm_->kNoError,
-              apm_->echo_cancellation()->GetDelayMetrics(&median, &std));
+              apm_->echo_cancellation()->GetDelayMetrics(
+                  &median, &std, &fraction_poor_delays));
 
     int rms_level = apm_->level_estimator()->RMS();
     EXPECT_LE(0, rms_level);
@@ -2079,6 +2087,8 @@
       audioproc::Test::DelayMetrics reference_delay = test->delay_metrics();
       EXPECT_NEAR(reference_delay.median(), median, kIntNear);
       EXPECT_NEAR(reference_delay.std(), std, kIntNear);
+      EXPECT_NEAR(reference_delay.fraction_poor_delays(), fraction_poor_delays,
+                  kFloatNear);
 
       EXPECT_NEAR(test->rms_level(), rms_level, kIntNear);
 
@@ -2109,6 +2119,7 @@
           test->mutable_delay_metrics();
       message_delay->set_median(median);
       message_delay->set_std(std);
+      message_delay->set_fraction_poor_delays(fraction_poor_delays);
 
       test->set_rms_level(rms_level);
 
diff --git a/webrtc/modules/audio_processing/test/process_test.cc b/webrtc/modules/audio_processing/test/process_test.cc
index 469ecf9..3af495c 100644
--- a/webrtc/modules/audio_processing/test/process_test.cc
+++ b/webrtc/modules/audio_processing/test/process_test.cc
@@ -1081,10 +1081,13 @@
     if (apm->echo_cancellation()->is_delay_logging_enabled()) {
       int median = 0;
       int std = 0;
-      apm->echo_cancellation()->GetDelayMetrics(&median, &std);
+      float fraction_poor_delays = 0;
+      apm->echo_cancellation()->GetDelayMetrics(&median, &std,
+                                                &fraction_poor_delays);
       printf("\n--Delay metrics--\n");
       printf("Median:             %3d\n", median);
       printf("Standard deviation: %3d\n", std);
+      printf("Poor delay values:  %3.1f%%\n", fraction_poor_delays * 100);
     }
   }
 
diff --git a/webrtc/modules/audio_processing/test/unittest.proto b/webrtc/modules/audio_processing/test/unittest.proto
index 47b962b..ddce46b 100644
--- a/webrtc/modules/audio_processing/test/unittest.proto
+++ b/webrtc/modules/audio_processing/test/unittest.proto
@@ -39,6 +39,7 @@
   message DelayMetrics {
     optional int32 median = 1;
     optional int32 std = 2;
+    optional float fraction_poor_delays = 3;
   }
 
   optional DelayMetrics delay_metrics = 12;