Bug 930603: Increase WebRTC AEC tail from 48ms to 128ms (rev 4837 at webrtc.org) r=jib
authorRandell Jesup <rjesup@jesup.org>
Fri, 25 Oct 2013 18:21:23 -0400
changeset 166124 0288124b22dd527171b4b13415511bcf4bcae677
parent 166123 5f38b1bd33582b5ef240209b41c75a0d6df06c14
child 166125 0e3e9ddedf98168ccc63de8e833541525f393ae3
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjib
bugs930603
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 930603: Increase WebRTC AEC tail from 48ms to 128ms (rev 4837 at webrtc.org) r=jib
media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c
media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.h
media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_internal.h
media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_sse2.c
media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation.c
media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation_internal.h
media/webrtc/trunk/webrtc/modules/audio_processing/aec/system_delay_unittest.cc
media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.cc
media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.h
media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl_unittest.cc
media/webrtc/trunk/webrtc/modules/modules.gyp
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c
@@ -104,17 +104,27 @@ const float WebRtcAec_overDriveCurve[65]
     1.8660f, 1.8750f, 1.8839f, 1.8927f, 1.9014f, 1.9100f,
     1.9186f, 1.9270f, 1.9354f, 1.9437f, 1.9520f, 1.9601f,
     1.9682f, 1.9763f, 1.9843f, 1.9922f, 2.0000f
 };
 
 // Target suppression levels for nlp modes.
 // log{0.001, 0.00001, 0.00000001}
 static const float kTargetSupp[3] = { -6.9f, -11.5f, -18.4f };
-static const float kMinOverDrive[3] = { 1.0f, 2.0f, 5.0f };
+
+// Two sets of parameters, one for the extended filter mode.
+static const float kExtendedMinOverDrive[3] = { 3.0f, 6.0f, 15.0f };
+static const float kNormalMinOverDrive[3] = { 1.0f, 2.0f, 5.0f };
+static const float kExtendedSmoothingCoefficients[2][2] =
+    { { 0.9f, 0.1f }, { 0.92f, 0.08f } };
+static const float kNormalSmoothingCoefficients[2][2] =
+    { { 0.9f, 0.1f }, { 0.93f, 0.07f } };
+
+// Number of partitions forming the NLP's "preferred" bands.
+enum { kPrefBandSize = 24 };
 
 #ifdef WEBRTC_AEC_DEBUG_DUMP
 extern int webrtc_aec_instance_count;
 #endif
 
 // "Private" function prototypes.
 static void ProcessBlock(AecCore* aec);
 
@@ -276,89 +286,92 @@ int WebRtcAec_FreeAec(AecCore* aec)
 
     free(aec);
     return 0;
 }
 
 static void FilterFar(AecCore* aec, float yf[2][PART_LEN1])
 {
   int i;
-  for (i = 0; i < NR_PART; i++) {
+  for (i = 0; i < aec->num_partitions; i++) {
     int j;
     int xPos = (i + aec->xfBufBlockPos) * PART_LEN1;
     int pos = i * PART_LEN1;
     // Check for wrap
-    if (i + aec->xfBufBlockPos >= NR_PART) {
-      xPos -= NR_PART*(PART_LEN1);
+    if (i + aec->xfBufBlockPos >= aec->num_partitions) {
+      xPos -= aec->num_partitions*(PART_LEN1);
     }
 
     for (j = 0; j < PART_LEN1; j++) {
       yf[0][j] += MulRe(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j],
                         aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]);
       yf[1][j] += MulIm(aec->xfBuf[0][xPos + j], aec->xfBuf[1][xPos + j],
                         aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]);
     }
   }
 }
 
 static void ScaleErrorSignal(AecCore* aec, float ef[2][PART_LEN1])
 {
+  const float mu = aec->extended_filter_enabled ? kExtendedMu : aec->normal_mu;
+  const float error_threshold = aec->extended_filter_enabled ?
+      kExtendedErrorThreshold : aec->normal_error_threshold;
   int i;
-  float absEf;
+  float abs_ef;
   for (i = 0; i < (PART_LEN1); i++) {
     ef[0][i] /= (aec->xPow[i] + 1e-10f);
     ef[1][i] /= (aec->xPow[i] + 1e-10f);
-    absEf = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]);
+    abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]);
 
-    if (absEf > aec->errThresh) {
-      absEf = aec->errThresh / (absEf + 1e-10f);
-      ef[0][i] *= absEf;
-      ef[1][i] *= absEf;
+    if (abs_ef > error_threshold) {
+      abs_ef = error_threshold / (abs_ef + 1e-10f);
+      ef[0][i] *= abs_ef;
+      ef[1][i] *= abs_ef;
     }
 
     // Stepsize factor
-    ef[0][i] *= aec->mu;
-    ef[1][i] *= aec->mu;
+    ef[0][i] *= mu;
+    ef[1][i] *= mu;
   }
 }
 
 // Time-unconstrined filter adaptation.
 // TODO(andrew): consider for a low-complexity mode.
 //static void FilterAdaptationUnconstrained(AecCore* aec, float *fft,
 //                                          float ef[2][PART_LEN1]) {
 //  int i, j;
-//  for (i = 0; i < NR_PART; i++) {
+//  for (i = 0; i < aec->num_partitions; i++) {
 //    int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
 //    int pos;
 //    // Check for wrap
-//    if (i + aec->xfBufBlockPos >= NR_PART) {
-//      xPos -= NR_PART * PART_LEN1;
+//    if (i + aec->xfBufBlockPos >= aec->num_partitions) {
+//      xPos -= aec->num_partitions * PART_LEN1;
 //    }
 //
 //    pos = i * PART_LEN1;
 //
 //    for (j = 0; j < PART_LEN1; j++) {
-//      aec->wfBuf[pos + j][0] += MulRe(aec->xfBuf[xPos + j][0],
-//                                      -aec->xfBuf[xPos + j][1],
-//                                      ef[j][0], ef[j][1]);
-//      aec->wfBuf[pos + j][1] += MulIm(aec->xfBuf[xPos + j][0],
-//                                      -aec->xfBuf[xPos + j][1],
-//                                      ef[j][0], ef[j][1]);
+//      aec->wfBuf[0][pos + j] += MulRe(aec->xfBuf[0][xPos + j],
+//                                      -aec->xfBuf[1][xPos + j],
+//                                      ef[0][j], ef[1][j]);
+//      aec->wfBuf[1][pos + j] += MulIm(aec->xfBuf[0][xPos + j],
+//                                      -aec->xfBuf[1][xPos + j],
+//                                      ef[0][j], ef[1][j]);
 //    }
 //  }
 //}
 
 static void FilterAdaptation(AecCore* aec, float *fft, float ef[2][PART_LEN1]) {
   int i, j;
-  for (i = 0; i < NR_PART; i++) {
+  for (i = 0; i < aec->num_partitions; i++) {
     int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
     int pos;
     // Check for wrap
-    if (i + aec->xfBufBlockPos >= NR_PART) {
-      xPos -= NR_PART * PART_LEN1;
+    if (i + aec->xfBufBlockPos >= aec->num_partitions) {
+      xPos -= aec->num_partitions * PART_LEN1;
     }
 
     pos = i * PART_LEN1;
 
     for (j = 0; j < PART_LEN; j++) {
 
       fft[2 * j] = MulRe(aec->xfBuf[0][xPos + j],
                          -aec->xfBuf[1][xPos + j],
@@ -422,22 +435,22 @@ WebRtcAec_OverdriveAndSuppress_t WebRtcA
 
 int WebRtcAec_InitAec(AecCore* aec, int sampFreq)
 {
     int i;
 
     aec->sampFreq = sampFreq;
 
     if (sampFreq == 8000) {
-        aec->mu = 0.6f;
-        aec->errThresh = 2e-6f;
+        aec->normal_mu = 0.6f;
+        aec->normal_error_threshold = 2e-6f;
     }
     else {
-        aec->mu = 0.5f;
-        aec->errThresh = 1.5e-6f;
+        aec->normal_mu = 0.5f;
+        aec->normal_error_threshold = 1.5e-6f;
     }
 
     if (WebRtc_InitBuffer(aec->nearFrBuf) == -1) {
         return -1;
     }
 
     if (WebRtc_InitBuffer(aec->outFrBuf) == -1) {
         return -1;
@@ -469,26 +482,29 @@ int WebRtcAec_InitAec(AecCore* aec, int 
       return -1;
     }
     if (WebRtc_InitDelayEstimator(aec->delay_estimator) != 0) {
       return -1;
     }
     aec->delay_logging_enabled = 0;
     memset(aec->delay_histogram, 0, sizeof(aec->delay_histogram));
 
+    aec->extended_filter_enabled = 0;
+    aec->num_partitions = kNormalNumPartitions;
+
     // Default target suppression mode.
     aec->nlp_mode = 1;
 
     // Sampling frequency multiplier
     // SWB is processed as 160 frame size
     if (aec->sampFreq == 32000) {
       aec->mult = (short)aec->sampFreq / 16000;
     }
     else {
-        aec->mult = (short)aec->sampFreq / 8000;
+      aec->mult = (short)aec->sampFreq / 8000;
     }
 
     aec->farBufWritePos = 0;
     aec->farBufReadPos = 0;
 
     aec->inSamples = 0;
     aec->outSamples = 0;
     aec->knownDelay = 0;
@@ -509,21 +525,24 @@ int WebRtcAec_InitAec(AecCore* aec, int 
     for (i = 0; i < PART_LEN1; i++) {
         aec->dMinPow[i] = 1.0e6f;
     }
 
     // Holds the last block written to
     aec->xfBufBlockPos = 0;
     // TODO: Investigate need for these initializations. Deleting them doesn't
     //       change the output at all and yields 0.4% overall speedup.
-    memset(aec->xfBuf, 0, sizeof(complex_t) * NR_PART * PART_LEN1);
-    memset(aec->wfBuf, 0, sizeof(complex_t) * NR_PART * PART_LEN1);
+    memset(aec->xfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions *
+        PART_LEN1);
+    memset(aec->wfBuf, 0, sizeof(complex_t) * kExtendedNumPartitions *
+        PART_LEN1);
     memset(aec->sde, 0, sizeof(complex_t) * PART_LEN1);
     memset(aec->sxd, 0, sizeof(complex_t) * PART_LEN1);
-    memset(aec->xfwBuf, 0, sizeof(complex_t) * NR_PART * PART_LEN1);
+    memset(aec->xfwBuf, 0, sizeof(complex_t) * kExtendedNumPartitions *
+        PART_LEN1);
     memset(aec->se, 0, sizeof(float) * PART_LEN1);
 
     // To prevent numerical instability in the first block.
     for (i = 0; i < PART_LEN1; i++) {
         aec->sd[i] = 1;
     }
     for (i = 0; i < PART_LEN1; i++) {
         aec->sx[i] = 1;
@@ -729,60 +748,63 @@ int WebRtcAec_GetDelayMetricsCore(AecCor
 
   // Reset histogram.
   memset(self->delay_histogram, 0, sizeof(self->delay_histogram));
 
   return 0;
 }
 
 int WebRtcAec_echo_state(AecCore* self) {
-  assert(self != NULL);
   return self->echoState;
 }
 
 void WebRtcAec_GetEchoStats(AecCore* self, Stats* erl, Stats* erle,
                             Stats* a_nlp) {
-  assert(self != NULL);
   assert(erl != NULL);
   assert(erle != NULL);
   assert(a_nlp != NULL);
   *erl = self->erl;
   *erle = self->erle;
   *a_nlp = self->aNlp;
 }
 
 #ifdef WEBRTC_AEC_DEBUG_DUMP
 void* WebRtcAec_far_time_buf(AecCore* self) {
-  assert(self != NULL);
   return self->far_time_buf;
 }
 #endif
 
 void WebRtcAec_SetConfigCore(AecCore* self, int nlp_mode, int metrics_mode,
                              int delay_logging) {
-  assert(self != NULL);
   assert(nlp_mode >= 0 && nlp_mode < 3);
   self->nlp_mode = nlp_mode;
   self->metricsMode = metrics_mode;
   if (self->metricsMode) {
     InitMetrics(self);
   }
   self->delay_logging_enabled = delay_logging;
   if (self->delay_logging_enabled) {
     memset(self->delay_histogram, 0, sizeof(self->delay_histogram));
   }
 }
 
+void WebRtcAec_enable_delay_correction(AecCore* self, int enable) {
+  self->extended_filter_enabled = enable;
+  self->num_partitions = enable ? kExtendedNumPartitions : kNormalNumPartitions;
+}
+
+int WebRtcAec_delay_correction_enabled(AecCore* self) {
+  return self->extended_filter_enabled;
+}
+
 int WebRtcAec_system_delay(AecCore* self) {
-  assert(self != NULL);
   return self->system_delay;
 }
 
 void WebRtcAec_SetSystemDelay(AecCore* self, int delay) {
-  assert(self != NULL);
   assert(delay >= 0);
   self->system_delay = delay;
 }
 
 static void ProcessBlock(AecCore* aec) {
     int i;
     float d[PART_LEN], y[PART_LEN], e[PART_LEN], dH[PART_LEN];
     float scale;
@@ -848,17 +870,18 @@ static void ProcessBlock(AecCore* aec) {
     // Near fft
     memcpy(fft, aec->dBuf, sizeof(float) * PART_LEN2);
     TimeToFrequency(fft, df, 0);
 
     // Power smoothing
     for (i = 0; i < PART_LEN1; i++) {
       far_spectrum = (xf_ptr[i] * xf_ptr[i]) +
           (xf_ptr[PART_LEN1 + i] * xf_ptr[PART_LEN1 + i]);
-      aec->xPow[i] = gPow[0] * aec->xPow[i] + gPow[1] * NR_PART * far_spectrum;
+      aec->xPow[i] = gPow[0] * aec->xPow[i] + gPow[1] * aec->num_partitions *
+          far_spectrum;
       // Calculate absolute spectra
       abs_far_spectrum[i] = sqrtf(far_spectrum);
 
       near_spectrum = df[0][i] * df[0][i] + df[1][i] * df[1][i];
       aec->dPow[i] = gPow[0] * aec->dPow[i] + gPow[1] * near_spectrum;
       // Calculate absolute spectra
       abs_near_spectrum[i] = sqrtf(near_spectrum);
     }
@@ -908,17 +931,17 @@ static void ProcessBlock(AecCore* aec) {
           aec->delay_histogram[delay_estimate]++;
         }
       }
     }
 
     // Update the xfBuf block position.
     aec->xfBufBlockPos--;
     if (aec->xfBufBlockPos == -1) {
-        aec->xfBufBlockPos = NR_PART - 1;
+        aec->xfBufBlockPos = aec->num_partitions - 1;
     }
 
     // Buffer xf
     memcpy(aec->xfBuf[0] + aec->xfBufBlockPos * PART_LEN1, xf_ptr,
            sizeof(float) * PART_LEN1);
     memcpy(aec->xfBuf[1] + aec->xfBufBlockPos * PART_LEN1, &xf_ptr[PART_LEN1],
            sizeof(float) * PART_LEN1);
 
@@ -1009,28 +1032,31 @@ static void NonLinearProcessing(AecCore*
     float scale, dtmp;
     float nlpGainHband;
     int i, j, pos;
 
     // Coherence and non-linear filter
     float cohde[PART_LEN1], cohxd[PART_LEN1];
     float hNlDeAvg, hNlXdAvg;
     float hNl[PART_LEN1];
-    float hNlPref[PREF_BAND_SIZE];
+    float hNlPref[kPrefBandSize];
     float hNlFb = 0, hNlFbLow = 0;
     const float prefBandQuant = 0.75f, prefBandQuantLow = 0.5f;
-    const int prefBandSize = PREF_BAND_SIZE / aec->mult;
+    const int prefBandSize = kPrefBandSize / aec->mult;
     const int minPrefBand = 4 / aec->mult;
 
     // Near and error power sums
     float sdSum = 0, seSum = 0;
 
-    // Power estimate smoothing coefficients
-    const float gCoh[2][2] = {{0.9f, 0.1f}, {0.93f, 0.07f}};
-    const float *ptrGCoh = gCoh[aec->mult - 1];
+    // Power estimate smoothing coefficients.
+    const float *ptrGCoh = aec->extended_filter_enabled ?
+        kExtendedSmoothingCoefficients[aec->mult - 1] :
+        kNormalSmoothingCoefficients[aec->mult - 1];
+    const float* min_overdrive = aec->extended_filter_enabled ?
+        kExtendedMinOverDrive : kNormalMinOverDrive;
 
     // Filter energy
     float wfEnMax = 0, wfEn = 0;
     const int delayEstInterval = 10 * aec->mult;
 
     float* xfw_ptr = NULL;
 
     aec->delayEstCtr++;
@@ -1043,17 +1069,17 @@ static void NonLinearProcessing(AecCore*
     nlpGainHband = (float)0.0;
     dtmp = (float)0.0;
 
     // Measure energy in each filter partition to determine delay.
     // TODO: Spread by computing one partition per block?
     if (aec->delayEstCtr == 0) {
         wfEnMax = 0;
         aec->delayIdx = 0;
-        for (i = 0; i < NR_PART; i++) {
+        for (i = 0; i < aec->num_partitions; i++) {
             pos = i * PART_LEN1;
             wfEn = 0;
             for (j = 0; j < PART_LEN1; j++) {
                 wfEn += aec->wfBuf[0][pos + j] * aec->wfBuf[0][pos + j] +
                     aec->wfBuf[1][pos + j] * aec->wfBuf[1][pos + j];
             }
 
             if (wfEn > wfEnMax) {
@@ -1184,17 +1210,17 @@ static void NonLinearProcessing(AecCore*
         aec->stNearState = 1;
     }
     else if (hNlDeAvg < 0.95f || hNlXdAvg < 0.8f) {
         aec->stNearState = 0;
     }
 
     if (aec->hNlXdAvgMin == 1) {
         aec->echoState = 0;
-        aec->overDrive = kMinOverDrive[aec->nlp_mode];
+        aec->overDrive = min_overdrive[aec->nlp_mode];
 
         if (aec->stNearState == 1) {
             memcpy(hNl, cohde, sizeof(hNl));
             hNlFb = hNlDeAvg;
             hNlFbLow = hNlDeAvg;
         }
         else {
             for (i = 0; i < PART_LEN1; i++) {
@@ -1240,17 +1266,17 @@ static void NonLinearProcessing(AecCore*
     if (aec->hNlNewMin == 1) {
         aec->hNlMinCtr++;
     }
     if (aec->hNlMinCtr == 2) {
         aec->hNlNewMin = 0;
         aec->hNlMinCtr = 0;
         aec->overDrive = WEBRTC_SPL_MAX(kTargetSupp[aec->nlp_mode] /
             ((float)log(aec->hNlFbMin + 1e-10f) + 1e-10f),
-            kMinOverDrive[aec->nlp_mode]);
+            min_overdrive[aec->nlp_mode]);
     }
 
     // Smooth the overdrive.
     if (aec->overDrive < aec->overDriveSm) {
       aec->overDriveSm = 0.99f * aec->overDriveSm + 0.01f * aec->overDrive;
     }
     else {
       aec->overDriveSm = 0.9f * aec->overDriveSm + 0.1f * aec->overDrive;
@@ -1460,17 +1486,16 @@ static void InitStats(Stats* stats) {
   stats->sum = 0;
   stats->hisum = 0;
   stats->himean = kOffsetLevel;
   stats->counter = 0;
   stats->hicounter = 0;
 }
 
 static void InitMetrics(AecCore* self) {
-  assert(self != NULL);
   self->stateCounter = 0;
   InitLevel(&self->farlevel);
   InitLevel(&self->nearlevel);
   InitLevel(&self->linoutlevel);
   InitLevel(&self->nlpoutlevel);
 
   InitStats(&self->erl);
   InitStats(&self->erle);
@@ -1682,8 +1707,9 @@ static void TimeToFrequency(float time_d
   freq_data[1][PART_LEN] = 0;
   freq_data[0][0] = time_data[0];
   freq_data[0][PART_LEN] = time_data[1];
   for (i = 1; i < PART_LEN; i++) {
     freq_data[0][i] = time_data[2 * i];
     freq_data[1][i] = time_data[2 * i + 1];
   }
 }
+
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.h
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.h
@@ -16,18 +16,16 @@
 #define WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_H_
 
 #include "webrtc/typedefs.h"
 
 #define FRAME_LEN 80
 #define PART_LEN 64  // Length of partition
 #define PART_LEN1 (PART_LEN + 1)  // Unique fft coefficients
 #define PART_LEN2 (PART_LEN * 2)  // Length of partition * 2
-#define NR_PART 12  // Number of partitions in filter.
-#define PREF_BAND_SIZE 24
 
 // Delay estimator constants, used for logging.
 enum { kMaxDelayBlocks = 60 };
 enum { kLookaheadBlocks = 15 };
 enum { kHistorySizeBlocks = kMaxDelayBlocks + kLookaheadBlocks };
 
 typedef float complex_t[2];
 // For performance reasons, some arrays of complex numbers are replaced by twice
@@ -67,31 +65,46 @@ void WebRtcAec_ProcessFrame(AecCore* aec
                             int knownDelay,
                             int16_t* out,
                             int16_t* outH);
 
 // A helper function to call WebRtc_MoveReadPtr() for all far-end buffers.
 // Returns the number of elements moved, and adjusts |system_delay| by the
 // 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);
+
 // Returns the echo state (1: echo, 0: no echo).
 int WebRtcAec_echo_state(AecCore* self);
+
 // Gets statistics of the echo metrics ERL, ERLE, A_NLP.
 void WebRtcAec_GetEchoStats(AecCore* self, Stats* erl, Stats* erle,
                             Stats* a_nlp);
 #ifdef WEBRTC_AEC_DEBUG_DUMP
 void* WebRtcAec_far_time_buf(AecCore* self);
 #endif
+
 // Sets local configuration modes.
 void WebRtcAec_SetConfigCore(AecCore* self, int nlp_mode, int metrics_mode,
                              int delay_logging);
+
+// We now interpret delay correction to mean an extended filter length feature.
+// We reuse the delay correction infrastructure to avoid changes through to
+// libjingle. See details along with |DelayCorrection| in
+// echo_cancellation_impl.h. Non-zero enables, zero disables.
+void WebRtcAec_enable_delay_correction(AecCore* self, int enable);
+
+// Returns non-zero if delay correction is enabled and zero if disabled.
+int WebRtcAec_delay_correction_enabled(AecCore* self);
+
 // Returns the current |system_delay|, i.e., the buffered difference between
 // far-end and near-end.
 int WebRtcAec_system_delay(AecCore* self);
+
 // Sets the |system_delay| to |value|.  Note that if the value is changed
 // improperly, there can be a performance regression.  So it should be used with
 // care.
 void WebRtcAec_SetSystemDelay(AecCore* self, int delay);
 
 #endif  // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_AEC_CORE_H_
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_internal.h
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_internal.h
@@ -14,16 +14,26 @@
 #ifdef WEBRTC_AEC_DEBUG_DUMP
 #include <stdio.h>
 #endif
 
 #include "webrtc/modules/audio_processing/aec/aec_core.h"
 #include "webrtc/modules/audio_processing/utility/ring_buffer.h"
 #include "webrtc/typedefs.h"
 
+// Number of partitions for the extended filter mode. The first one is an enum
+// to be used in array declarations, as it represents the maximum filter length.
+enum { kExtendedNumPartitions = 32 };
+static const int kNormalNumPartitions = 12;
+
+// Extended filter adaptation parameters.
+// TODO(ajm): No narrowband tuning yet.
+static const float kExtendedMu = 0.4f;
+static const float kExtendedErrorThreshold = 1.0e-6f;
+
 typedef struct PowerLevel {
   float sfrsum;
   int sfrcounter;
   float framelevel;
   float frsum;
   int frcounter;
   float minlevel;
   float averagelevel;
@@ -48,21 +58,22 @@ struct AecCore {
   float dBufH[PART_LEN2];  // nearend
 
   float xPow[PART_LEN1];
   float dPow[PART_LEN1];
   float dMinPow[PART_LEN1];
   float dInitMinPow[PART_LEN1];
   float *noisePow;
 
-  float xfBuf[2][NR_PART * PART_LEN1];  // farend fft buffer
-  float wfBuf[2][NR_PART * PART_LEN1];  // filter fft
+  float xfBuf[2][kExtendedNumPartitions * PART_LEN1];  // farend fft buffer
+  float wfBuf[2][kExtendedNumPartitions * PART_LEN1];  // filter fft
   complex_t sde[PART_LEN1];  // cross-psd of nearend and error
   complex_t sxd[PART_LEN1];  // cross-psd of farend and nearend
-  complex_t xfwBuf[NR_PART * PART_LEN1];  // farend windowed fft buffer
+  // Farend windowed fft buffer.
+  complex_t xfwBuf[kExtendedNumPartitions * PART_LEN1];
 
   float sx[PART_LEN1], sd[PART_LEN1], se[PART_LEN1];  // far, near, error psd
   float hNs[PART_LEN1];
   float hNlFbMin, hNlFbLocalMin;
   float hNlXdAvgMin;
   int hNlNewMin, hNlMinCtr;
   float overDrive, overDriveSm;
   int nlp_mode;
@@ -77,18 +88,18 @@ struct AecCore {
   RingBuffer* far_buf;
   RingBuffer* far_buf_windowed;
   int system_delay;  // Current system delay buffered in AEC.
 
   int mult;  // sampling frequency multiple
   int sampFreq;
   uint32_t seed;
 
-  float mu;  // stepsize
-  float errThresh;  // error threshold
+  float normal_mu;  // stepsize
+  float normal_error_threshold;  // error threshold
 
   int noiseEstCtr;
 
   PowerLevel farlevel;
   PowerLevel nearlevel;
   PowerLevel linoutlevel;
   PowerLevel nlpoutlevel;
 
@@ -104,16 +115,21 @@ struct AecCore {
   int flag_Hband_cn;  // for comfort noise
   float cn_scale_Hband;  // scale for comfort noise in H band
 
   int delay_histogram[kHistorySizeBlocks];
   int delay_logging_enabled;
   void* delay_estimator_farend;
   void* delay_estimator;
 
+  // 1 = extended filter mode enabled, 0 = disabled.
+  int extended_filter_enabled;
+  // Runtime selection of number of filter partitions.
+  int num_partitions;
+
 #ifdef WEBRTC_AEC_DEBUG_DUMP
   RingBuffer* far_time_buf;
   FILE *farFile;
   FILE *nearFile;
   FILE *outFile;
   FILE *outLinearFile;
 #endif
 };
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_sse2.c
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core_sse2.c
@@ -29,23 +29,24 @@
 __inline static float MulIm(float aRe, float aIm, float bRe, float bIm)
 {
   return aRe * bIm + aIm * bRe;
 }
 
 static void FilterFarSSE2(AecCore* aec, float yf[2][PART_LEN1])
 {
   int i;
-  for (i = 0; i < NR_PART; i++) {
+  const int num_partitions = aec->num_partitions;
+  for (i = 0; i < num_partitions; i++) {
     int j;
     int xPos = (i + aec->xfBufBlockPos) * PART_LEN1;
     int pos = i * PART_LEN1;
     // Check for wrap
-    if (i + aec->xfBufBlockPos >= NR_PART) {
-      xPos -= NR_PART*(PART_LEN1);
+    if (i + aec->xfBufBlockPos >= num_partitions) {
+      xPos -= num_partitions*(PART_LEN1);
     }
 
     // vectorized code (four at once)
     for (j = 0; j + 3 < PART_LEN1; j += 4) {
       const __m128 xfBuf_re = _mm_loadu_ps(&aec->xfBuf[0][xPos + j]);
       const __m128 xfBuf_im = _mm_loadu_ps(&aec->xfBuf[1][xPos + j]);
       const __m128 wfBuf_re = _mm_loadu_ps(&aec->wfBuf[0][pos + j]);
       const __m128 wfBuf_im = _mm_loadu_ps(&aec->wfBuf[1][pos + j]);
@@ -70,18 +71,21 @@ static void FilterFarSSE2(AecCore* aec, 
                         aec->wfBuf[0][ pos + j], aec->wfBuf[1][ pos + j]);
     }
   }
 }
 
 static void ScaleErrorSignalSSE2(AecCore* aec, float ef[2][PART_LEN1])
 {
   const __m128 k1e_10f = _mm_set1_ps(1e-10f);
-  const __m128 kThresh = _mm_set1_ps(aec->errThresh);
-  const __m128 kMu = _mm_set1_ps(aec->mu);
+  const __m128 kMu = aec->extended_filter_enabled ?
+      _mm_set1_ps(kExtendedMu) : _mm_set1_ps(aec->normal_mu);
+  const __m128 kThresh = aec->extended_filter_enabled ?
+      _mm_set1_ps(kExtendedErrorThreshold) :
+      _mm_set1_ps(aec->normal_error_threshold);
 
   int i;
   // vectorized code (four at once)
   for (i = 0; i + 3 < PART_LEN1; i += 4) {
     const __m128 xPow = _mm_loadu_ps(&aec->xPow[i]);
     const __m128 ef_re_base = _mm_loadu_ps(&ef[0][i]);
     const __m128 ef_im_base = _mm_loadu_ps(&ef[1][i]);
 
@@ -105,42 +109,49 @@ static void ScaleErrorSignalSSE2(AecCore
     ef_im = _mm_or_ps(ef_im, ef_im_if);
     ef_re = _mm_mul_ps(ef_re, kMu);
     ef_im = _mm_mul_ps(ef_im, kMu);
 
     _mm_storeu_ps(&ef[0][i], ef_re);
     _mm_storeu_ps(&ef[1][i], ef_im);
   }
   // scalar code for the remaining items.
-  for (; i < (PART_LEN1); i++) {
-    float absEf;
-    ef[0][i] /= (aec->xPow[i] + 1e-10f);
-    ef[1][i] /= (aec->xPow[i] + 1e-10f);
-    absEf = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]);
+  {
+    const float mu = aec->extended_filter_enabled ?
+        kExtendedMu : aec->normal_mu;
+    const float error_threshold = aec->extended_filter_enabled ?
+        kExtendedErrorThreshold : aec->normal_error_threshold;
+    for (; i < (PART_LEN1); i++) {
+    float abs_ef;
+      ef[0][i] /= (aec->xPow[i] + 1e-10f);
+      ef[1][i] /= (aec->xPow[i] + 1e-10f);
+      abs_ef = sqrtf(ef[0][i] * ef[0][i] + ef[1][i] * ef[1][i]);
 
-    if (absEf > aec->errThresh) {
-      absEf = aec->errThresh / (absEf + 1e-10f);
-      ef[0][i] *= absEf;
-      ef[1][i] *= absEf;
+      if (abs_ef > error_threshold) {
+        abs_ef = error_threshold / (abs_ef + 1e-10f);
+        ef[0][i] *= abs_ef;
+        ef[1][i] *= abs_ef;
+      }
+
+      // Stepsize factor
+      ef[0][i] *= mu;
+      ef[1][i] *= mu;
     }
-
-    // Stepsize factor
-    ef[0][i] *= aec->mu;
-    ef[1][i] *= aec->mu;
   }
 }
 
 static void FilterAdaptationSSE2(AecCore* aec, float *fft, float ef[2][PART_LEN1]) {
   int i, j;
-  for (i = 0; i < NR_PART; i++) {
+  const int num_partitions = aec->num_partitions;
+  for (i = 0; i < num_partitions; i++) {
     int xPos = (i + aec->xfBufBlockPos)*(PART_LEN1);
     int pos = i * PART_LEN1;
     // Check for wrap
-    if (i + aec->xfBufBlockPos >= NR_PART) {
-      xPos -= NR_PART * PART_LEN1;
+    if (i + aec->xfBufBlockPos >= num_partitions) {
+      xPos -= num_partitions * PART_LEN1;
     }
 
     // Process the whole array...
     for (j = 0; j < PART_LEN; j+= 4) {
       // Load xfBuf and ef.
       const __m128 xfBuf_re = _mm_loadu_ps(&aec->xfBuf[0][xPos + j]);
       const __m128 xfBuf_im = _mm_loadu_ps(&aec->xfBuf[1][xPos + j]);
       const __m128 ef_re = _mm_loadu_ps(&ef[0][j]);
@@ -408,8 +419,9 @@ static void OverdriveAndSuppressSSE2(Aec
 }
 
 void WebRtcAec_InitAec_SSE2(void) {
   WebRtcAec_FilterFar = FilterFarSSE2;
   WebRtcAec_ScaleErrorSignal = ScaleErrorSignalSSE2;
   WebRtcAec_FilterAdaptation = FilterAdaptationSSE2;
   WebRtcAec_OverdriveAndSuppress = OverdriveAndSuppressSSE2;
 }
+
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation.c
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation.c
@@ -22,33 +22,95 @@
 
 #include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
 #include "webrtc/modules/audio_processing/aec/aec_core.h"
 #include "webrtc/modules/audio_processing/aec/aec_resampler.h"
 #include "webrtc/modules/audio_processing/aec/echo_cancellation_internal.h"
 #include "webrtc/modules/audio_processing/utility/ring_buffer.h"
 #include "webrtc/typedefs.h"
 
+// Measured delays [ms]
+// Device                Chrome  GTP
+// MacBook Air           10
+// MacBook Retina        10      100
+// MacPro                30?
+//
+// Win7 Desktop          70      80?
+// Win7 T430s            110
+// Win8 T420s            70
+//
+// Daisy                 50
+// Pixel (w/ preproc?)           240
+// Pixel (w/o preproc?)  110     110
+
+// The extended filter mode gives us the flexibility to ignore the system's
+// reported delays. We do this for platforms which we believe provide results
+// which are incompatible with the AEC's expectations. Based on measurements
+// (some provided above) we set a conservative (i.e. lower than measured)
+// fixed delay.
+//
+// WEBRTC_UNTRUSTED_DELAY will only have an impact when |extended_filter_mode|
+// is enabled. See the note along with |DelayCorrection| in
+// echo_cancellation_impl.h for more details on the mode.
+//
+// Justification:
+// Chromium/Mac: Here, the true latency is so low (~10-20 ms), that it plays
+// havoc with the AEC's buffering. To avoid this, we set a fixed delay of 20 ms
+// and then compensate by rewinding by 10 ms (in wideband) through
+// kDelayDiffOffsetSamples. This trick does not seem to work for larger rewind
+// values, but fortunately this is sufficient.
+//
+// Chromium/Linux(ChromeOS): The values we get on this platform don't correspond
+// well to reality. The variance doesn't match the AEC's buffer changes, and the
+// bulk values tend to be too low. However, the range across different hardware
+// appears to be too large to choose a single value.
+//
+// GTP/Linux(ChromeOS): TBD, but for the moment we will trust the values.
+#if defined(WEBRTC_CHROMIUM_BUILD) && defined(WEBRTC_MAC)
+#define WEBRTC_UNTRUSTED_DELAY
+#endif
+
+#if defined(WEBRTC_MAC)
+static const int kFixedDelayMs = 20;
+static const int kDelayDiffOffsetSamples = -160;
+#elif defined(WEBRTC_WIN)
+static const int kFixedDelayMs = 50;
+static const int kDelayDiffOffsetSamples = 0;
+#else
+// Essentially ChromeOS.
+static const int kFixedDelayMs = 50;
+static const int kDelayDiffOffsetSamples = 0;
+#endif
+static const int kMinTrustedDelayMs = 20;
+static const int kMaxTrustedDelayMs = 500;
+
 // Maximum length of resampled signal. Must be an integer multiple of frames
 // (ceil(1/(1 + MIN_SKEW)*2) + 1)*FRAME_LEN
 // The factor of 2 handles wb, and the + 1 is as a safety margin
 // TODO(bjornv): Replace with kResamplerBufferSize
 #define MAX_RESAMP_LEN (5 * FRAME_LEN)
 
 static const int kMaxBufSizeStart = 62;  // In partitions
 static const int sampMsNb = 8; // samples per ms in nb
 static const int initCheck = 42;
 
 #ifdef WEBRTC_AEC_DEBUG_DUMP
 int webrtc_aec_instance_count = 0;
 #endif
 
 // Estimates delay to set the position of the far-end buffer read pointer
 // (controlled by knownDelay)
-static int EstBufDelay(aecpc_t *aecInst);
+static void EstBufDelayNormal(aecpc_t *aecInst);
+static void EstBufDelayExtended(aecpc_t *aecInst);
+static int ProcessNormal(aecpc_t* self, const int16_t* near,
+    const int16_t* near_high, int16_t* out, int16_t* out_high,
+    int16_t num_samples, int16_t reported_delay_ms, int32_t skew);
+static void ProcessExtended(aecpc_t* self, const int16_t* near,
+    const int16_t* near_high, int16_t* out, int16_t* out_high,
+    int16_t num_samples, int16_t reported_delay_ms, int32_t skew);
 
 int32_t WebRtcAec_Create(void **aecInst)
 {
     aecpc_t *aecpc;
     if (aecInst == NULL) {
         return -1;
     }
 
@@ -130,20 +192,16 @@ int32_t WebRtcAec_Free(void *aecInst)
     return 0;
 }
 
 int32_t WebRtcAec_Init(void *aecInst, int32_t sampFreq, int32_t scSampFreq)
 {
     aecpc_t *aecpc = aecInst;
     AecConfig aecConfig;
 
-    if (aecpc == NULL) {
-        return -1;
-    }
-
     if (sampFreq != 8000 && sampFreq != 16000  && sampFreq != 32000) {
         aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
         return -1;
     }
     aecpc->sampFreq = sampFreq;
 
     if (scSampFreq < 1 || scSampFreq > 96000) {
         aecpc->lastError = AEC_BAD_PARAMETER_ERROR;
@@ -172,41 +230,41 @@ int32_t WebRtcAec_Init(void *aecInst, in
 
     if (aecpc->sampFreq == 32000) {
         aecpc->splitSampFreq = 16000;
     }
     else {
         aecpc->splitSampFreq = sampFreq;
     }
 
-    aecpc->skewFrCtr = 0;
-    aecpc->activity = 0;
-
     aecpc->delayCtr = 0;
+    aecpc->sampFactor = (aecpc->scSampFreq * 1.0f) / aecpc->splitSampFreq;
+    // Sampling frequency multiplier (SWB is processed as 160 frame size).
+    aecpc->rate_factor = aecpc->splitSampFreq / 8000;
 
     aecpc->sum = 0;
     aecpc->counter = 0;
     aecpc->checkBuffSize = 1;
     aecpc->firstVal = 0;
 
-    aecpc->ECstartup = 1;
+    aecpc->startup_phase = 1;
     aecpc->bufSizeStart = 0;
     aecpc->checkBufSizeCtr = 0;
-    aecpc->filtDelay = 0;
+    aecpc->msInSndCardBuf = 0;
+    aecpc->filtDelay = -1;  // -1 indicates an initialized state.
     aecpc->timeForDelayChange = 0;
     aecpc->knownDelay = 0;
     aecpc->lastDelayDiff = 0;
 
-    aecpc->skew = 0;
+    aecpc->skewFrCtr = 0;
     aecpc->resample = kAecFalse;
     aecpc->highSkewCtr = 0;
-    aecpc->sampFactor = (aecpc->scSampFreq * 1.0f) / aecpc->splitSampFreq;
+    aecpc->skew = 0;
 
-    // Sampling frequency multiplier (SWB is processed as 160 frame size).
-    aecpc->rate_factor = aecpc->splitSampFreq / 8000;
+    aecpc->farend_started = 0;
 
     // Default settings.
     aecConfig.nlpMode = kAecNlpModerate;
     aecConfig.skewMode = kAecFalse;
     aecConfig.metricsMode = kAecFalse;
     aecConfig.delay_logging = kAecFalse;
 
     if (WebRtcAec_set_config(aecpc, aecConfig) == -1) {
@@ -234,20 +292,16 @@ int32_t WebRtcAec_BufferFarend(void *aec
     int newNrOfSamples = (int) nrOfSamples;
     short newFarend[MAX_RESAMP_LEN];
     const int16_t* farend_ptr = farend;
     float tmp_farend[MAX_RESAMP_LEN];
     const float* farend_float = tmp_farend;
     float skew;
     int i = 0;
 
-    if (aecpc == NULL) {
-        return -1;
-    }
-
     if (farend == NULL) {
         aecpc->lastError = AEC_NULL_POINTER_ERROR;
         return -1;
     }
 
     if (aecpc->initFlag != initCheck) {
         aecpc->lastError = AEC_UNINITIALIZED_ERROR;
         return -1;
@@ -263,16 +317,17 @@ int32_t WebRtcAec_BufferFarend(void *aec
 
     if (aecpc->skewMode == kAecTrue && aecpc->resample == kAecTrue) {
         // Resample and get a new number of samples
         WebRtcAec_ResampleLinear(aecpc->resampler, farend, nrOfSamples, skew,
                                  newFarend, &newNrOfSamples);
         farend_ptr = (const int16_t*) newFarend;
     }
 
+    aecpc->farend_started = 1;
     WebRtcAec_SetSystemDelay(aecpc->aec, WebRtcAec_system_delay(aecpc->aec) +
                              newNrOfSamples);
 
 #ifdef WEBRTC_AEC_DEBUG_DUMP
     WebRtc_WriteBuffer(aecpc->far_pre_buf_s16, farend_ptr,
                        (size_t) newNrOfSamples);
 #endif
     // Cast to float and write the time-domain data to |far_pre_buf|.
@@ -306,27 +361,16 @@ int32_t WebRtcAec_BufferFarend(void *aec
 
 int32_t WebRtcAec_Process(void *aecInst, const int16_t *nearend,
                           const int16_t *nearendH, int16_t *out, int16_t *outH,
                           int16_t nrOfSamples, int16_t msInSndCardBuf,
                           int32_t skew)
 {
     aecpc_t *aecpc = aecInst;
     int32_t retVal = 0;
-    short i;
-    short nBlocks10ms;
-    short nFrames;
-    // Limit resampling to doubling/halving of signal
-    const float minSkewEst = -0.5f;
-    const float maxSkewEst = 1.0f;
-
-    if (aecpc == NULL) {
-        return -1;
-    }
-
     if (nearend == NULL) {
         aecpc->lastError = AEC_NULL_POINTER_ERROR;
         return -1;
     }
 
     if (out == NULL) {
         aecpc->lastError = AEC_NULL_POINTER_ERROR;
         return -1;
@@ -349,154 +393,31 @@ int32_t WebRtcAec_Process(void *aecInst,
        return -1;
     }
 
     if (msInSndCardBuf < 0) {
         msInSndCardBuf = 0;
         aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
         retVal = -1;
     }
-    else if (msInSndCardBuf > 500) {
-        msInSndCardBuf = 500;
+    else if (msInSndCardBuf > kMaxTrustedDelayMs) {
+        // The clamping is now done in ProcessExtended/Normal().
         aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
         retVal = -1;
     }
-    // TODO(andrew): we need to investigate if this +10 is really wanted.
-    msInSndCardBuf += 10;
-    aecpc->msInSndCardBuf = msInSndCardBuf;
 
-    if (aecpc->skewMode == kAecTrue) {
-        if (aecpc->skewFrCtr < 25) {
-            aecpc->skewFrCtr++;
-        }
-        else {
-            retVal = WebRtcAec_GetSkew(aecpc->resampler, skew, &aecpc->skew);
-            if (retVal == -1) {
-                aecpc->skew = 0;
-                aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
-            }
-
-            aecpc->skew /= aecpc->sampFactor*nrOfSamples;
-
-            if (aecpc->skew < 1.0e-3 && aecpc->skew > -1.0e-3) {
-                aecpc->resample = kAecFalse;
-            }
-            else {
-                aecpc->resample = kAecTrue;
-            }
-
-            if (aecpc->skew < minSkewEst) {
-                aecpc->skew = minSkewEst;
-            }
-            else if (aecpc->skew > maxSkewEst) {
-                aecpc->skew = maxSkewEst;
-            }
-
-#ifdef WEBRTC_AEC_DEBUG_DUMP
-            (void)fwrite(&aecpc->skew, sizeof(aecpc->skew), 1, aecpc->skewFile);
-#endif
-        }
-    }
-
-    nFrames = nrOfSamples / FRAME_LEN;
-    nBlocks10ms = nFrames / aecpc->rate_factor;
-
-    if (aecpc->ECstartup) {
-        if (nearend != out) {
-            // Only needed if they don't already point to the same place.
-            memcpy(out, nearend, sizeof(short) * nrOfSamples);
-        }
-
-        // The AEC is in the start up mode
-        // AEC is disabled until the system delay is OK
-
-        // Mechanism to ensure that the system delay is reasonably stable.
-        if (aecpc->checkBuffSize) {
-            aecpc->checkBufSizeCtr++;
-            // Before we fill up the far-end buffer we require the system delay
-            // to be stable (+/-8 ms) compared to the first value. This
-            // comparison is made during the following 6 consecutive 10 ms
-            // blocks. If it seems to be stable then we start to fill up the
-            // far-end buffer.
-            if (aecpc->counter == 0) {
-                aecpc->firstVal = aecpc->msInSndCardBuf;
-                aecpc->sum = 0;
-            }
-
-            if (abs(aecpc->firstVal - aecpc->msInSndCardBuf) <
-                WEBRTC_SPL_MAX(0.2 * aecpc->msInSndCardBuf, sampMsNb)) {
-                aecpc->sum += aecpc->msInSndCardBuf;
-                aecpc->counter++;
-            }
-            else {
-                aecpc->counter = 0;
-            }
-
-            if (aecpc->counter * nBlocks10ms >= 6) {
-                // The far-end buffer size is determined in partitions of
-                // PART_LEN samples. Use 75% of the average value of the system
-                // delay as buffer size to start with.
-                aecpc->bufSizeStart = WEBRTC_SPL_MIN((3 * aecpc->sum *
-                  aecpc->rate_factor * 8) / (4 * aecpc->counter * PART_LEN),
-                  kMaxBufSizeStart);
-                // Buffer size has now been determined.
-                aecpc->checkBuffSize = 0;
-            }
-
-            if (aecpc->checkBufSizeCtr * nBlocks10ms > 50) {
-                // For really bad systems, don't disable the echo canceller for
-                // more than 0.5 sec.
-                aecpc->bufSizeStart = WEBRTC_SPL_MIN((aecpc->msInSndCardBuf *
-                    aecpc->rate_factor * 3) / 40, kMaxBufSizeStart);
-                aecpc->checkBuffSize = 0;
-            }
-        }
-
-        // If |checkBuffSize| changed in the if-statement above.
-        if (!aecpc->checkBuffSize) {
-            // The system delay is now reasonably stable (or has been unstable
-            // for too long). When the far-end buffer is filled with
-            // approximately the same amount of data as reported by the system
-            // we end the startup phase.
-            int overhead_elements =
-                WebRtcAec_system_delay(aecpc->aec) / PART_LEN -
-                aecpc->bufSizeStart;
-            if (overhead_elements == 0) {
-                // Enable the AEC
-                aecpc->ECstartup = 0;
-            } else if (overhead_elements > 0) {
-                // TODO(bjornv): Do we need a check on how much we actually
-                // moved the read pointer? It should always be possible to move
-                // the pointer |overhead_elements| since we have only added data
-                // to the buffer and no delay compensation nor AEC processing
-                // has been done.
-                WebRtcAec_MoveFarReadPtr(aecpc->aec, overhead_elements);
-
-                // Enable the AEC
-                aecpc->ECstartup = 0;
-            }
-        }
+    // This returns the value of aec->extended_filter_enabled.
+    if (WebRtcAec_delay_correction_enabled(aecpc->aec)) {
+      ProcessExtended(aecpc, nearend, nearendH, out, outH, nrOfSamples,
+                      msInSndCardBuf, skew);
     } else {
-        // AEC is enabled.
-
-        EstBufDelay(aecpc);
-
-        // Note that 1 frame is supported for NB and 2 frames for WB.
-        for (i = 0; i < nFrames; i++) {
-            // Call the AEC.
-            WebRtcAec_ProcessFrame(aecpc->aec,
-                                   &nearend[FRAME_LEN * i],
-                                   &nearendH[FRAME_LEN * i],
-                                   aecpc->knownDelay,
-                                   &out[FRAME_LEN * i],
-                                   &outH[FRAME_LEN * i]);
-            // TODO(bjornv): Re-structure such that we don't have to pass
-            // |aecpc->knownDelay| as input. Change name to something like
-            // |system_buffer_diff|.
-        }
+      if (ProcessNormal(aecpc, nearend, nearendH, out, outH, nrOfSamples,
+                        msInSndCardBuf, skew) != 0) {
+        retVal = -1;
+      }
     }
 
 #ifdef WEBRTC_AEC_DEBUG_DUMP
     {
         int16_t far_buf_size_ms = (int16_t)(WebRtcAec_system_delay(aecpc->aec) /
             (sampMsNb * aecpc->rate_factor));
         (void)fwrite(&far_buf_size_ms, 2, 1, aecpc->bufFile);
         (void)fwrite(&aecpc->knownDelay, sizeof(aecpc->knownDelay), 1,
@@ -504,21 +425,16 @@ int32_t WebRtcAec_Process(void *aecInst,
     }
 #endif
 
     return retVal;
 }
 
 int WebRtcAec_set_config(void* handle, AecConfig config) {
   aecpc_t* self = (aecpc_t*)handle;
-
-  if (handle == NULL ) {
-    return -1;
-  }
-
   if (self->initFlag != initCheck) {
     self->lastError = AEC_UNINITIALIZED_ERROR;
     return -1;
   }
 
   if (config.skewMode != kAecFalse && config.skewMode != kAecTrue) {
     self->lastError = AEC_BAD_PARAMETER_ERROR;
     return -1;
@@ -543,20 +459,16 @@ int WebRtcAec_set_config(void* handle, A
 
   WebRtcAec_SetConfigCore(self->aec, config.nlpMode, config.metricsMode,
                           config.delay_logging);
   return 0;
 }
 
 int WebRtcAec_get_echo_status(void* handle, int* status) {
   aecpc_t* self = (aecpc_t*)handle;
-
-  if (handle == NULL ) {
-    return -1;
-  }
   if (status == NULL ) {
     self->lastError = AEC_NULL_POINTER_ERROR;
     return -1;
   }
   if (self->initFlag != initCheck) {
     self->lastError = AEC_UNINITIALIZED_ERROR;
     return -1;
   }
@@ -660,20 +572,16 @@ int WebRtcAec_GetMetrics(void* handle, A
     metrics->aNlp.min = kOffsetLevel;
   }
 
   return 0;
 }
 
 int WebRtcAec_GetDelayMetrics(void* handle, int* median, int* std) {
   aecpc_t* self = handle;
-
-  if (handle == NULL) {
-    return -1;
-  }
   if (median == NULL) {
     self->lastError = AEC_NULL_POINTER_ERROR;
     return -1;
   }
   if (std == NULL) {
     self->lastError = AEC_NULL_POINTER_ERROR;
     return -1;
   }
@@ -688,32 +596,240 @@ int WebRtcAec_GetDelayMetrics(void* hand
   }
 
   return 0;
 }
 
 int32_t WebRtcAec_get_error_code(void *aecInst)
 {
     aecpc_t *aecpc = aecInst;
-
-    if (aecpc == NULL) {
-        return -1;
-    }
-
     return aecpc->lastError;
 }
 
 AecCore* WebRtcAec_aec_core(void* handle) {
   if (!handle) {
     return NULL;
   }
   return ((aecpc_t*) handle)->aec;
 }
 
-static int EstBufDelay(aecpc_t* aecpc) {
+static int ProcessNormal(aecpc_t *aecpc, const int16_t *nearend,
+                         const int16_t *nearendH, int16_t *out, int16_t *outH,
+                         int16_t nrOfSamples, int16_t msInSndCardBuf,
+                         int32_t skew) {
+  int retVal = 0;
+  short i;
+  short nBlocks10ms;
+  short nFrames;
+  // Limit resampling to doubling/halving of signal
+  const float minSkewEst = -0.5f;
+  const float maxSkewEst = 1.0f;
+
+  msInSndCardBuf = msInSndCardBuf > kMaxTrustedDelayMs ?
+      kMaxTrustedDelayMs : msInSndCardBuf;
+  // TODO(andrew): we need to investigate if this +10 is really wanted.
+  msInSndCardBuf += 10;
+  aecpc->msInSndCardBuf = msInSndCardBuf;
+
+  if (aecpc->skewMode == kAecTrue) {
+    if (aecpc->skewFrCtr < 25) {
+      aecpc->skewFrCtr++;
+    }
+    else {
+      retVal = WebRtcAec_GetSkew(aecpc->resampler, skew, &aecpc->skew);
+      if (retVal == -1) {
+        aecpc->skew = 0;
+        aecpc->lastError = AEC_BAD_PARAMETER_WARNING;
+      }
+
+      aecpc->skew /= aecpc->sampFactor*nrOfSamples;
+
+      if (aecpc->skew < 1.0e-3 && aecpc->skew > -1.0e-3) {
+        aecpc->resample = kAecFalse;
+      }
+      else {
+        aecpc->resample = kAecTrue;
+      }
+
+      if (aecpc->skew < minSkewEst) {
+        aecpc->skew = minSkewEst;
+      }
+      else if (aecpc->skew > maxSkewEst) {
+        aecpc->skew = maxSkewEst;
+      }
+
+#ifdef WEBRTC_AEC_DEBUG_DUMP
+      (void)fwrite(&aecpc->skew, sizeof(aecpc->skew), 1, aecpc->skewFile);
+#endif
+    }
+  }
+
+  nFrames = nrOfSamples / FRAME_LEN;
+  nBlocks10ms = nFrames / aecpc->rate_factor;
+
+  if (aecpc->startup_phase) {
+    // Only needed if they don't already point to the same place.
+    if (nearend != out) {
+      memcpy(out, nearend, sizeof(short) * nrOfSamples);
+    }
+    if (nearendH != outH) {
+      memcpy(outH, nearendH, sizeof(short) * nrOfSamples);
+    }
+
+    // The AEC is in the start up mode
+    // AEC is disabled until the system delay is OK
+
+    // Mechanism to ensure that the system delay is reasonably stable.
+    if (aecpc->checkBuffSize) {
+      aecpc->checkBufSizeCtr++;
+      // Before we fill up the far-end buffer we require the system delay
+      // to be stable (+/-8 ms) compared to the first value. This
+      // comparison is made during the following 6 consecutive 10 ms
+      // blocks. If it seems to be stable then we start to fill up the
+      // far-end buffer.
+      if (aecpc->counter == 0) {
+        aecpc->firstVal = aecpc->msInSndCardBuf;
+        aecpc->sum = 0;
+      }
+
+      if (abs(aecpc->firstVal - aecpc->msInSndCardBuf) <
+        WEBRTC_SPL_MAX(0.2 * aecpc->msInSndCardBuf, sampMsNb)) {
+        aecpc->sum += aecpc->msInSndCardBuf;
+        aecpc->counter++;
+      }
+      else {
+        aecpc->counter = 0;
+      }
+
+      if (aecpc->counter * nBlocks10ms >= 6) {
+        // The far-end buffer size is determined in partitions of
+        // PART_LEN samples. Use 75% of the average value of the system
+        // delay as buffer size to start with.
+        aecpc->bufSizeStart = WEBRTC_SPL_MIN((3 * aecpc->sum *
+            aecpc->rate_factor * 8) / (4 * aecpc->counter * PART_LEN),
+            kMaxBufSizeStart);
+        // Buffer size has now been determined.
+        aecpc->checkBuffSize = 0;
+      }
+
+      if (aecpc->checkBufSizeCtr * nBlocks10ms > 50) {
+        // For really bad systems, don't disable the echo canceller for
+        // more than 0.5 sec.
+        aecpc->bufSizeStart = WEBRTC_SPL_MIN((aecpc->msInSndCardBuf *
+            aecpc->rate_factor * 3) / 40, kMaxBufSizeStart);
+        aecpc->checkBuffSize = 0;
+      }
+    }
+
+    // If |checkBuffSize| changed in the if-statement above.
+    if (!aecpc->checkBuffSize) {
+      // The system delay is now reasonably stable (or has been unstable
+      // for too long). When the far-end buffer is filled with
+      // approximately the same amount of data as reported by the system
+      // we end the startup phase.
+      int overhead_elements =
+          WebRtcAec_system_delay(aecpc->aec) / PART_LEN - aecpc->bufSizeStart;
+      if (overhead_elements == 0) {
+        // Enable the AEC
+        aecpc->startup_phase = 0;
+      } else if (overhead_elements > 0) {
+        // TODO(bjornv): Do we need a check on how much we actually
+        // moved the read pointer? It should always be possible to move
+        // the pointer |overhead_elements| since we have only added data
+        // to the buffer and no delay compensation nor AEC processing
+        // has been done.
+        WebRtcAec_MoveFarReadPtr(aecpc->aec, overhead_elements);
+
+        // Enable the AEC
+        aecpc->startup_phase = 0;
+      }
+    }
+  } else {
+    // AEC is enabled.
+    EstBufDelayNormal(aecpc);
+
+    // Note that 1 frame is supported for NB and 2 frames for WB.
+    for (i = 0; i < nFrames; i++) {
+      // Call the AEC.
+      WebRtcAec_ProcessFrame(aecpc->aec,
+                             &nearend[FRAME_LEN * i],
+                             &nearendH[FRAME_LEN * i],
+                             aecpc->knownDelay,
+                             &out[FRAME_LEN * i],
+                             &outH[FRAME_LEN * i]);
+      // TODO(bjornv): Re-structure such that we don't have to pass
+      // |aecpc->knownDelay| as input. Change name to something like
+      // |system_buffer_diff|.
+    }
+  }
+
+  return retVal;
+}
+
+static void ProcessExtended(aecpc_t* self, const int16_t* near,
+    const int16_t* near_high, int16_t* out, int16_t* out_high,
+    int16_t num_samples, int16_t reported_delay_ms, int32_t skew) {
+  int i;
+  const int num_frames = num_samples / FRAME_LEN;
+#if defined(WEBRTC_UNTRUSTED_DELAY)
+  const int delay_diff_offset = kDelayDiffOffsetSamples;
+  reported_delay_ms = kFixedDelayMs;
+#else
+  // This is the usual mode where we trust the reported system delay values.
+  const int delay_diff_offset = 0;
+  // Due to the longer filter, we no longer add 10 ms to the reported delay
+  // to reduce chance of non-causality. Instead we apply a minimum here to avoid
+  // issues with the read pointer jumping around needlessly.
+  reported_delay_ms = reported_delay_ms < kMinTrustedDelayMs ?
+      kMinTrustedDelayMs : reported_delay_ms;
+  // If the reported delay appears to be bogus, we attempt to recover by using
+  // the measured fixed delay values. We use >= here because higher layers
+  // may already clamp to this maximum value, and we would otherwise not
+  // detect it here.
+  reported_delay_ms = reported_delay_ms >= kMaxTrustedDelayMs ?
+      kFixedDelayMs : reported_delay_ms;
+#endif
+  self->msInSndCardBuf = reported_delay_ms;
+
+  if (!self->farend_started) {
+    // Only needed if they don't already point to the same place.
+    if (near != out) {
+      memcpy(out, near, sizeof(short) * num_samples);
+    }
+    if (near_high != out_high) {
+      memcpy(out_high, near_high, sizeof(short) * num_samples);
+    }
+    return;
+  }
+  if (self->startup_phase) {
+    // In the extended mode, there isn't a startup "phase", just a special
+    // action on the first frame. In the trusted delay case, we'll take the
+    // current reported delay, unless it's less then our conservative
+    // measurement.
+    int startup_size_ms = reported_delay_ms < kFixedDelayMs ?
+        kFixedDelayMs : reported_delay_ms;
+    int overhead_elements = (WebRtcAec_system_delay(self->aec) -
+        startup_size_ms / 2 * self->rate_factor * 8) / PART_LEN;
+    WebRtcAec_MoveFarReadPtr(self->aec, overhead_elements);
+    self->startup_phase = 0;
+  }
+
+  EstBufDelayExtended(self);
+
+  for (i = 0; i < num_frames; ++i) {
+    // |delay_diff_offset| gives us the option to manually rewind the delay on
+    // very low delay platforms which can't be expressed purely through
+    // |reported_delay_ms|.
+    WebRtcAec_ProcessFrame(self->aec, &near[FRAME_LEN * i],
+        &near_high[FRAME_LEN * i], self->knownDelay + delay_diff_offset,
+        &out[FRAME_LEN * i], &out_high[FRAME_LEN * i]);
+  }
+}
+
+static void EstBufDelayNormal(aecpc_t* aecpc) {
   int nSampSndCard = aecpc->msInSndCardBuf * sampMsNb * aecpc->rate_factor;
   int current_delay = nSampSndCard - WebRtcAec_system_delay(aecpc->aec);
   int delay_difference = 0;
 
   // Before we proceed with the delay estimate filtering we:
   // 1) Compensate for the frame that will be read.
   // 2) Compensate for drift resampling.
   // 3) Compensate for non-causality if needed, since the estimated delay can't
@@ -727,18 +843,21 @@ static int EstBufDelay(aecpc_t* aecpc) {
     current_delay -= kResamplingDelay;
   }
 
   // 3) Compensate for non-causality, if needed, by flushing one block.
   if (current_delay < PART_LEN) {
     current_delay += WebRtcAec_MoveFarReadPtr(aecpc->aec, 1) * PART_LEN;
   }
 
+  // We use -1 to signal an initialized state in the "extended" implementation;
+  // compensate for that.
+  aecpc->filtDelay = aecpc->filtDelay < 0 ? 0 : aecpc->filtDelay;
   aecpc->filtDelay = WEBRTC_SPL_MAX(0, (short) (0.8 * aecpc->filtDelay +
-          0.2 * current_delay));
+      0.2 * current_delay));
 
   delay_difference = aecpc->filtDelay - aecpc->knownDelay;
   if (delay_difference > 224) {
     if (aecpc->lastDelayDiff < 96) {
       aecpc->timeForDelayChange = 0;
     } else {
       aecpc->timeForDelayChange++;
     }
@@ -751,11 +870,63 @@ static int EstBufDelay(aecpc_t* aecpc) {
   } else {
     aecpc->timeForDelayChange = 0;
   }
   aecpc->lastDelayDiff = delay_difference;
 
   if (aecpc->timeForDelayChange > 25) {
     aecpc->knownDelay = WEBRTC_SPL_MAX((int) aecpc->filtDelay - 160, 0);
   }
+}
 
-  return 0;
+static void EstBufDelayExtended(aecpc_t* self) {
+  int reported_delay = self->msInSndCardBuf * sampMsNb * self->rate_factor;
+  int current_delay = reported_delay - WebRtcAec_system_delay(self->aec);
+  int delay_difference = 0;
+
+  // Before we proceed with the delay estimate filtering we:
+  // 1) Compensate for the frame that will be read.
+  // 2) Compensate for drift resampling.
+  // 3) Compensate for non-causality if needed, since the estimated delay can't
+  //    be negative.
+
+  // 1) Compensating for the frame(s) that will be read/processed.
+  current_delay += FRAME_LEN * self->rate_factor;
+
+  // 2) Account for resampling frame delay.
+  if (self->skewMode == kAecTrue && self->resample == kAecTrue) {
+    current_delay -= kResamplingDelay;
+  }
+
+  // 3) Compensate for non-causality, if needed, by flushing two blocks.
+  if (current_delay < PART_LEN) {
+    current_delay += WebRtcAec_MoveFarReadPtr(self->aec, 2) * PART_LEN;
+  }
+
+  if (self->filtDelay == -1) {
+    self->filtDelay = WEBRTC_SPL_MAX(0, 0.5 * current_delay);
+  } else {
+    self->filtDelay = WEBRTC_SPL_MAX(0, (short) (0.95 * self->filtDelay +
+        0.05 * current_delay));
+  }
+
+  delay_difference = self->filtDelay - self->knownDelay;
+  if (delay_difference > 384) {
+    if (self->lastDelayDiff < 128) {
+      self->timeForDelayChange = 0;
+    } else {
+      self->timeForDelayChange++;
+    }
+  } else if (delay_difference < 128 && self->knownDelay > 0) {
+    if (self->lastDelayDiff > 384) {
+      self->timeForDelayChange = 0;
+    } else {
+      self->timeForDelayChange++;
+    }
+  } else {
+    self->timeForDelayChange = 0;
+  }
+  self->lastDelayDiff = delay_difference;
+
+  if (self->timeForDelayChange > 25) {
+    self->knownDelay = WEBRTC_SPL_MAX((int) self->filtDelay - 256, 0);
+  }
 }
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation_internal.h
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/aec/echo_cancellation_internal.h
@@ -15,18 +15,16 @@
 #include "webrtc/modules/audio_processing/utility/ring_buffer.h"
 
 typedef struct {
   int delayCtr;
   int sampFreq;
   int splitSampFreq;
   int scSampFreq;
   float sampFactor;  // scSampRate / sampFreq
-  short autoOnOff;
-  short activity;
   short skewMode;
   int bufSizeStart;
   int knownDelay;
   int rate_factor;
 
   short initFlag;  // indicates if AEC has been initialized
 
   // Variables used for averaging far end buffer size
@@ -34,17 +32,17 @@ typedef struct {
   int sum;
   short firstVal;
   short checkBufSizeCtr;
 
   // Variables used for delay shifts
   short msInSndCardBuf;
   short filtDelay;  // Filtered delay estimate.
   int timeForDelayChange;
-  int ECstartup;
+  int startup_phase;
   int checkBuffSize;
   short lastDelayDiff;
 
 #ifdef WEBRTC_AEC_DEBUG_DUMP
   RingBuffer* far_pre_buf_s16;  // Time domain far-end pre-buffer in int16_t.
   FILE* bufFile;
   FILE* delayFile;
   FILE* skewFile;
@@ -57,12 +55,14 @@ typedef struct {
   int resample;  // if the skew is small enough we don't resample
   int highSkewCtr;
   float skew;
 
   RingBuffer* far_pre_buf;  // Time domain far-end pre-buffer.
 
   int lastError;
 
+  int farend_started;
+
   AecCore* aec;
 } aecpc_t;
 
 #endif  // WEBRTC_MODULES_AUDIO_PROCESSING_AEC_ECHO_CANCELLATION_INTERNAL_H_
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/aec/system_delay_unittest.cc
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/aec/system_delay_unittest.cc
@@ -123,17 +123,17 @@ void SystemDelayTest::RunStableStartup()
   // Process().
   int buffer_size = BufferFillUp();
   // A stable device should be accepted and put in a regular process mode within
   // |kStableConvergenceMs|.
   int process_time_ms = 0;
   for (; process_time_ms < kStableConvergenceMs; process_time_ms += 10) {
     RenderAndCapture(kDeviceBufMs);
     buffer_size += samples_per_frame_;
-    if (self_->ECstartup == 0) {
+    if (self_->startup_phase == 0) {
       // We have left the startup phase.
       break;
     }
   }
   // Verify convergence time.
   EXPECT_GT(kStableConvergenceMs, process_time_ms);
   // Verify that the buffer has been flushed.
   EXPECT_GE(buffer_size, WebRtcAec_system_delay(self_->aec));
@@ -217,17 +217,17 @@ TEST_F(SystemDelayTest, CorrectDelayAfte
     int buffer_offset_ms = 25;
     int reported_delay_ms = 0;
     int process_time_ms = 0;
     for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) {
       reported_delay_ms = kDeviceBufMs + buffer_offset_ms;
       RenderAndCapture(reported_delay_ms);
       buffer_size += samples_per_frame_;
       buffer_offset_ms = -buffer_offset_ms;
-      if (self_->ECstartup == 0) {
+      if (self_->startup_phase == 0) {
         // We have left the startup phase.
         break;
       }
     }
     // Verify convergence time.
     EXPECT_GE(kMaxConvergenceMs, process_time_ms);
     // Verify that the buffer has been flushed.
     EXPECT_GE(buffer_size, WebRtcAec_system_delay(self_->aec));
@@ -263,17 +263,17 @@ TEST_F(SystemDelayTest, CorrectDelayAfte
     // We now have established the required buffer size. Let us verify that we
     // fill up before leaving the startup phase for normal processing.
     int buffer_size = 0;
     int target_buffer_size = kDeviceBufMs * samples_per_frame_ / 10 * 3 / 4;
     process_time_ms = 0;
     for (; process_time_ms <= kMaxConvergenceMs; process_time_ms += 10) {
       RenderAndCapture(kDeviceBufMs);
       buffer_size += samples_per_frame_;
-      if (self_->ECstartup == 0) {
+      if (self_->startup_phase == 0) {
         // We have left the startup phase.
         break;
       }
     }
     // Verify convergence time.
     EXPECT_GT(kMaxConvergenceMs, process_time_ms);
     // Verify that the buffer has reached the desired size.
     EXPECT_LE(target_buffer_size, WebRtcAec_system_delay(self_->aec));
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.cc
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.cc
@@ -8,22 +8,24 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
 #include "webrtc/modules/audio_processing/echo_cancellation_impl.h"
 
 #include <assert.h>
 #include <string.h>
 
+extern "C" {
+#include "webrtc/modules/audio_processing/aec/aec_core.h"
+}
+#include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h"
 #include "webrtc/modules/audio_processing/audio_buffer.h"
 #include "webrtc/modules/audio_processing/audio_processing_impl.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 
-#include "webrtc/modules/audio_processing/aec/include/echo_cancellation.h"
-
 namespace webrtc {
 
 typedef void Handle;
 
 namespace {
 int16_t MapSetting(EchoCancellation::SuppressionLevel level) {
   switch (level) {
     case EchoCancellation::kLowSuppression:
@@ -59,17 +61,18 @@ EchoCancellationImpl::EchoCancellationIm
     apm_(apm),
     drift_compensation_enabled_(false),
     metrics_enabled_(false),
     suppression_level_(kModerateSuppression),
     device_sample_rate_hz_(48000),
     stream_drift_samples_(0),
     was_stream_drift_set_(false),
     stream_has_echo_(false),
-    delay_logging_enabled_(false) {}
+    delay_logging_enabled_(false),
+    delay_correction_enabled_(true) {}
 
 EchoCancellationImpl::~EchoCancellationImpl() {}
 
 int EchoCancellationImpl::ProcessRenderAudio(const AudioBuffer* audio) {
   if (!is_component_enabled()) {
     return apm_->kNoError;
   }
 
@@ -328,16 +331,23 @@ int EchoCancellationImpl::Initialize() {
     return err;
   }
 
   was_stream_drift_set_ = false;
 
   return apm_->kNoError;
 }
 
+#if 0
+void EchoCancellationImpl::SetExtraOptions(const Config& config) {
+  delay_correction_enabled_ = config.Get<DelayCorrection>().enabled;
+  Configure();
+}
+#endif
+
 void* EchoCancellationImpl::CreateHandle() const {
   Handle* handle = NULL;
   if (WebRtcAec_Create(&handle) != apm_->kNoError) {
     handle = NULL;
   } else {
     assert(handle != NULL);
   }
 
@@ -359,16 +369,18 @@ int EchoCancellationImpl::InitializeHand
 int EchoCancellationImpl::ConfigureHandle(void* handle) const {
   assert(handle != NULL);
   AecConfig config;
   config.metricsMode = metrics_enabled_;
   config.nlpMode = MapSetting(suppression_level_);
   config.skewMode = drift_compensation_enabled_;
   config.delay_logging = delay_logging_enabled_;
 
+  WebRtcAec_enable_delay_correction(WebRtcAec_aec_core(
+      static_cast<Handle*>(handle)), delay_correction_enabled_ ? 1 : 0);
   return WebRtcAec_set_config(static_cast<Handle*>(handle), config);
 }
 
 int EchoCancellationImpl::num_handles_required() const {
   return apm_->num_output_channels() *
          apm_->num_reverse_channels();
 }
 
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.h
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl.h
@@ -10,16 +10,40 @@
 
 #ifndef WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_
 #define WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_
 
 #include "webrtc/modules/audio_processing/include/audio_processing.h"
 #include "webrtc/modules/audio_processing/processing_component.h"
 
 namespace webrtc {
+// Use to enable the delay correction feature. This now engages an extended
+// filter mode in the AEC, along with robustness measures around the reported
+// system delays. It comes with a significant increase in AEC complexity, but is
+// much more robust to unreliable reported delays.
+//
+// Detailed changes to the algorithm:
+// - The filter length is changed from 48 to 128 ms. This comes with tuning of
+//   several parameters: i) filter adaptation stepsize and error threshold;
+//   ii) non-linear processing smoothing and overdrive.
+// - Option to ignore the reported delays on platforms which we deem
+//   sufficiently unreliable. See WEBRTC_UNTRUSTED_DELAY in echo_cancellation.c.
+// - Faster startup times by removing the excessive "startup phase" processing
+//   of reported delays.
+// - Much more conservative adjustments to the far-end read pointer. We smooth
+//   the delay difference more heavily, and back off from the difference more.
+//   Adjustments force a readaptation of the filter, so they should be avoided
+//   except when really necessary.
+struct DelayCorrection {
+  DelayCorrection() : enabled(false) {}
+  DelayCorrection(bool enabled) : enabled(enabled) {}
+
+  bool enabled;
+};
+
 class AudioProcessingImpl;
 class AudioBuffer;
 
 class EchoCancellationImpl : public EchoCancellation,
                              public ProcessingComponent {
  public:
   explicit EchoCancellationImpl(const AudioProcessingImpl* apm);
   virtual ~EchoCancellationImpl();
@@ -29,16 +53,17 @@ class EchoCancellationImpl : public Echo
 
   // EchoCancellation implementation.
   virtual bool is_enabled() const;
   virtual int device_sample_rate_hz() const;
   virtual int stream_drift_samples() const;
 
   // ProcessingComponent implementation.
   virtual int Initialize();
+  //  virtual void SetExtraOptions(const Config& config) OVERRIDE;
 
  private:
   // EchoCancellation implementation.
   virtual int Enable(bool enable);
   virtual int enable_drift_compensation(bool enable);
   virtual bool is_drift_compensation_enabled() const;
   virtual int set_device_sample_rate_hz(int rate);
   virtual void set_stream_drift_samples(int drift);
@@ -65,12 +90,13 @@ class EchoCancellationImpl : public Echo
   bool drift_compensation_enabled_;
   bool metrics_enabled_;
   SuppressionLevel suppression_level_;
   int device_sample_rate_hz_;
   int stream_drift_samples_;
   bool was_stream_drift_set_;
   bool stream_has_echo_;
   bool delay_logging_enabled_;
+  bool delay_correction_enabled_;
 };
 }  // namespace webrtc
 
 #endif  // WEBRTC_MODULES_AUDIO_PROCESSING_ECHO_CANCELLATION_IMPL_H_
new file mode 100644
--- /dev/null
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/echo_cancellation_impl_unittest.cc
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "testing/gtest/include/gtest/gtest.h"
+extern "C" {
+#include "webrtc/modules/audio_processing/aec/aec_core.h"
+}
+#include "webrtc/modules/audio_processing/echo_cancellation_impl.h"
+#include "webrtc/modules/audio_processing/include/audio_processing.h"
+#include "webrtc/system_wrappers/interface/scoped_ptr.h"
+
+namespace webrtc {
+
+TEST(EchoCancellationInternalTest, DelayCorrection) {
+  scoped_ptr<AudioProcessing> ap(AudioProcessing::Create(0));
+  EXPECT_TRUE(ap->echo_cancellation()->aec_core() == NULL);
+
+  EXPECT_EQ(ap->kNoError, ap->echo_cancellation()->Enable(true));
+  EXPECT_TRUE(ap->echo_cancellation()->is_enabled());
+
+  AecCore* aec_core = ap->echo_cancellation()->aec_core();
+  ASSERT_TRUE(aec_core != NULL);
+  // Disabled by default.
+  EXPECT_EQ(0, WebRtcAec_delay_correction_enabled(aec_core));
+
+  Config config;
+  config.Set<DelayCorrection>(new DelayCorrection(true));
+  ap->SetExtraOptions(config);
+  EXPECT_EQ(1, WebRtcAec_delay_correction_enabled(aec_core));
+
+  // Retains setting after initialization.
+  EXPECT_EQ(ap->kNoError, ap->Initialize());
+  EXPECT_EQ(1, WebRtcAec_delay_correction_enabled(aec_core));
+
+  config.Set<DelayCorrection>(new DelayCorrection(false));
+  ap->SetExtraOptions(config);
+  EXPECT_EQ(0, WebRtcAec_delay_correction_enabled(aec_core));
+
+  // Retains setting after initialization.
+  EXPECT_EQ(ap->kNoError, ap->Initialize());
+  EXPECT_EQ(0, WebRtcAec_delay_correction_enabled(aec_core));
+}
+
+}  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/modules.gyp
+++ b/media/webrtc/trunk/webrtc/modules/modules.gyp
@@ -138,16 +138,17 @@
             'audio_coding/neteq4/mock/mock_delay_peak_detector.h',
             'audio_coding/neteq4/mock/mock_dtmf_buffer.h',
             'audio_coding/neteq4/mock/mock_dtmf_tone_generator.h',
             'audio_coding/neteq4/mock/mock_external_decoder_pcm16b.h',
             'audio_coding/neteq4/mock/mock_packet_buffer.h',
             'audio_coding/neteq4/mock/mock_payload_splitter.h',
             'audio_processing/aec/system_delay_unittest.cc',
             'audio_processing/aec/echo_cancellation_unittest.cc',
+            'audio_processing/echo_cancellation_impl_unittest.cc',
             'audio_processing/test/unit_test.cc',
             'audio_processing/utility/delay_estimator_unittest.cc',
             'audio_processing/utility/ring_buffer_unittest.cc',
             'bitrate_controller/bitrate_controller_unittest.cc',
             'desktop_capture/desktop_region_unittest.cc',
             'desktop_capture/differ_block_unittest.cc',
             'desktop_capture/differ_unittest.cc',
             'desktop_capture/screen_capturer_helper_unittest.cc',