Added calls to set and get external echo channels.
Review URL: http://webrtc-codereview.appspot.com/62005

git-svn-id: http://webrtc.googlecode.com/svn/trunk@196 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/src/modules/audio_processing/main/interface/audio_processing.h b/src/modules/audio_processing/main/interface/audio_processing.h
index dc9c232..4d5df2f 100644
--- a/src/modules/audio_processing/main/interface/audio_processing.h
+++ b/src/modules/audio_processing/main/interface/audio_processing.h
@@ -356,6 +356,20 @@
   virtual int enable_comfort_noise(bool enable) = 0;
   virtual bool is_comfort_noise_enabled() const = 0;
 
+  // A typical use case is to initialize the component with an echo path from a
+  // previous call. The echo path is retrieved using |GetEchoPath()| typically
+  // at the end of a call. The data can then be stored for later use as
+  // initializer, using |SetEchoPath()|.
+  // Controlling the echo path this way requires the data |size_bytes| to match
+  // the internal echo path size. This size can be acquired using
+  // |echo_path_size_bytes()|. |SetEchoPath()| causes an entire reset, worth
+  // noting if it is to be called during an ongoing call. It is possible that
+  // version incompatibilities may result in a stored echo path of the
+  // incorrect size. In this case, the stored path should be discarded.
+  virtual int SetEchoPath(const void* echo_path, int size_bytes) = 0;
+  virtual int GetEchoPath(void* echo_path, int size_bytes) const = 0;
+  virtual const int echo_path_size_bytes() const = 0;
+
  protected:
   virtual ~EchoControlMobile() {};
 };
diff --git a/src/modules/audio_processing/main/source/echo_control_mobile_impl.cc b/src/modules/audio_processing/main/source/echo_control_mobile_impl.cc
index 1cd2502..ce940eb 100644
--- a/src/modules/audio_processing/main/source/echo_control_mobile_impl.cc
+++ b/src/modules/audio_processing/main/source/echo_control_mobile_impl.cc
@@ -11,6 +11,7 @@
 #include "echo_control_mobile_impl.h"
 
 #include <cassert>
+#include <cstring>
 
 #include "critical_section_wrapper.h"
 #include "echo_control_mobile.h"
@@ -44,14 +45,15 @@
   switch (err) {
     case AECM_UNSUPPORTED_FUNCTION_ERROR:
       return AudioProcessing::kUnsupportedFunctionError;
+    case AECM_NULL_POINTER_ERROR:
+      return AudioProcessing::kNullPointerError;
     case AECM_BAD_PARAMETER_ERROR:
       return AudioProcessing::kBadParameterError;
     case AECM_BAD_PARAMETER_WARNING:
       return AudioProcessing::kBadStreamParameterWarning;
     default:
-      // AECMOBFIX_UNSPECIFIED_ERROR
-      // AECMOBFIX_UNINITIALIZED_ERROR
-      // AECMOBFIX_NULL_POINTER_ERROR
+      // AECM_UNSPECIFIED_ERROR
+      // AECM_UNINITIALIZED_ERROR
       return AudioProcessing::kUnspecifiedError;
   }
 }
@@ -61,9 +63,16 @@
   : ProcessingComponent(apm),
     apm_(apm),
     routing_mode_(kSpeakerphone),
-    comfort_noise_enabled_(true) {}
+    comfort_noise_enabled_(true),
+    echo_path_size_bytes_(WebRtcAecm_echo_path_size_bytes()),
+    external_echo_path_(NULL) {}
 
-EchoControlMobileImpl::~EchoControlMobileImpl() {}
+EchoControlMobileImpl::~EchoControlMobileImpl() {
+    if (external_echo_path_ != NULL) {
+      delete [] external_echo_path_;
+      external_echo_path_ = NULL;
+    }
+}
 
 int EchoControlMobileImpl::ProcessRenderAudio(const AudioBuffer* audio) {
   if (!is_component_enabled()) {
@@ -181,6 +190,52 @@
   return comfort_noise_enabled_;
 }
 
+int EchoControlMobileImpl::SetEchoPath(const void* echo_path,
+                                       int size_bytes) {
+  CriticalSectionScoped crit_scoped(*apm_->crit());
+  if (echo_path == NULL) {
+    return apm_->kNullPointerError;
+  }
+  if (size_bytes != echo_path_size_bytes_) {
+    // Size mismatch
+    return apm_->kBadParameterError;
+  }
+
+  if (external_echo_path_ == NULL) {
+    external_echo_path_ = new unsigned char[size_bytes];
+  }
+  memcpy(external_echo_path_, echo_path, size_bytes);
+
+  return Initialize();
+}
+
+int EchoControlMobileImpl::GetEchoPath(void* echo_path,
+                                       int size_bytes) const {
+  CriticalSectionScoped crit_scoped(*apm_->crit());
+  if (echo_path == NULL) {
+    return apm_->kNullPointerError;
+  }
+  if (size_bytes != echo_path_size_bytes_) {
+    // Size mismatch
+    return apm_->kBadParameterError;
+  }
+  if (!is_component_enabled()) {
+    return apm_->kNotEnabledError;
+  }
+
+  // Get the echo path from the first channel
+  Handle* my_handle = static_cast<Handle*>(handle(0));
+  if (WebRtcAecm_GetEchoPath(my_handle, echo_path, size_bytes) != 0) {
+      return GetHandleError(my_handle);
+  }
+
+  return apm_->kNoError;
+}
+
+const int EchoControlMobileImpl::echo_path_size_bytes() const {
+    return echo_path_size_bytes_;
+}
+
 int EchoControlMobileImpl::Initialize() {
   if (!is_component_enabled()) {
     return apm_->kNoError;
@@ -197,7 +252,7 @@
 int EchoControlMobileImpl::get_version(char* version,
                                        int version_len_bytes) const {
   if (WebRtcAecm_get_version(version, version_len_bytes) != 0) {
-      return apm_->kBadParameterError;
+    return apm_->kBadParameterError;
   }
 
   return apm_->kNoError;
@@ -219,10 +274,20 @@
 }
 
 int EchoControlMobileImpl::InitializeHandle(void* handle) const {
-  return WebRtcAecm_Init(static_cast<Handle*>(handle),
-                         apm_->sample_rate_hz(),
-                         48000); // Dummy value. This isn't actually
-                                 // required by AECM.
+  assert(handle != NULL);
+  Handle* my_handle = static_cast<Handle*>(handle);
+  if (WebRtcAecm_Init(my_handle, apm_->sample_rate_hz()) != 0) {
+    return GetHandleError(my_handle);
+  }
+  if (external_echo_path_ != NULL) {
+    if (WebRtcAecm_InitEchoPath(my_handle,
+                                external_echo_path_,
+                                echo_path_size_bytes_) != 0) {
+      return GetHandleError(my_handle);
+    }
+  }
+
+  return apm_->kNoError;
 }
 
 int EchoControlMobileImpl::ConfigureHandle(void* handle) const {
diff --git a/src/modules/audio_processing/main/source/echo_control_mobile_impl.h b/src/modules/audio_processing/main/source/echo_control_mobile_impl.h
index 2fd6248..5e5bd4e 100644
--- a/src/modules/audio_processing/main/source/echo_control_mobile_impl.h
+++ b/src/modules/audio_processing/main/source/echo_control_mobile_impl.h
@@ -41,6 +41,9 @@
   virtual RoutingMode routing_mode() const;
   virtual int enable_comfort_noise(bool enable);
   virtual bool is_comfort_noise_enabled() const;
+  virtual int SetEchoPath(const void* echo_path, int size_bytes);
+  virtual int GetEchoPath(void* echo_path, int size_bytes) const;
+  virtual const int echo_path_size_bytes() const;
 
   // ProcessingComponent implementation.
   virtual void* CreateHandle() const;
@@ -53,6 +56,8 @@
   const AudioProcessingImpl* apm_;
   RoutingMode routing_mode_;
   bool comfort_noise_enabled_;
+  const int echo_path_size_bytes_;
+  unsigned char* external_echo_path_;
 };
 }  // namespace webrtc
 
diff --git a/src/modules/audio_processing/main/test/process_test/process_test.cc b/src/modules/audio_processing/main/test/process_test/process_test.cc
index c62345f..2e659ec 100644
--- a/src/modules/audio_processing/main/test/process_test/process_test.cc
+++ b/src/modules/audio_processing/main/test/process_test/process_test.cc
@@ -56,6 +56,8 @@
   printf("  --drift_compensation\n");
   printf("  --no_drift_compensation\n");
   printf("\n  -aecm    Echo control mobile\n");
+  printf("  --aecm_echo_path_in_file FILE");
+  printf("  --aecm_echo_path_out_file FILE");
   printf("\n  -agc     Gain control\n");
   printf("  --analog\n");
   printf("  --adaptive_digital\n");
@@ -103,6 +105,8 @@
   const char* near_filename = NULL;
   const char* out_filename = NULL;
   const char* vad_out_filename = NULL;
+  const char* aecm_echo_path_in_filename = NULL;
+  const char* aecm_echo_path_out_filename = NULL;
 
   int32_t sample_rate_hz = 16000;
   int32_t device_sample_rate_hz = 16000;
@@ -185,6 +189,16 @@
     } else if (strcmp(argv[i], "-aecm") == 0) {
       ASSERT_EQ(apm->kNoError, apm->echo_control_mobile()->Enable(true));
 
+    } else if (strcmp(argv[i], "--aecm_echo_path_in_file") == 0) {
+      i++;
+      ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_in_file";
+      aecm_echo_path_in_filename = argv[i];
+
+    } else if (strcmp(argv[i], "--aecm_echo_path_out_file") == 0) {
+      i++;
+      ASSERT_LT(i, argc) << "Specify filename after --aecm_echo_path_out_file";
+      aecm_echo_path_out_filename = argv[i];
+
     } else if (strcmp(argv[i], "-agc") == 0) {
       ASSERT_EQ(apm->kNoError, apm->gain_control()->Enable(true));
 
@@ -323,6 +337,8 @@
   FILE* delay_file = NULL;
   FILE* drift_file = NULL;
   FILE* vad_out_file = NULL;
+  FILE* aecm_echo_path_in_file = NULL;
+  FILE* aecm_echo_path_out_file = NULL;
 
   if (far_filename != NULL) {
     far_file = fopen(far_filename, "rb");
@@ -361,6 +377,30 @@
                                       << vad_out_file;
   }
 
+  if (aecm_echo_path_in_filename != NULL) {
+    aecm_echo_path_in_file = fopen(aecm_echo_path_in_filename, "rb");
+    ASSERT_TRUE(NULL != aecm_echo_path_in_file) << "Unable to open file "
+                                                << aecm_echo_path_in_filename;
+
+    const int path_size = apm->echo_control_mobile()->echo_path_size_bytes();
+    unsigned char echo_path[path_size];
+    ASSERT_EQ(path_size, fread(echo_path,
+                               sizeof(unsigned char),
+                               path_size,
+                               aecm_echo_path_in_file));
+    EXPECT_EQ(apm->kNoError,
+              apm->echo_control_mobile()->SetEchoPath(echo_path, path_size));
+    fclose(aecm_echo_path_in_file);
+    aecm_echo_path_in_file = NULL;
+  }
+
+  if (aecm_echo_path_out_filename != NULL) {
+    aecm_echo_path_out_file = fopen(aecm_echo_path_out_filename, "wb");
+    ASSERT_TRUE(NULL != aecm_echo_path_out_file) << "Unable to open file "
+                                                 << aecm_echo_path_out_filename;
+
+  }
+
   enum Events {
     kInitializeEvent,
     kRenderEvent,
@@ -579,6 +619,18 @@
     }
   }
 
+  if (aecm_echo_path_out_file != NULL) {
+    const int path_size = apm->echo_control_mobile()->echo_path_size_bytes();
+    unsigned char echo_path[path_size];
+    apm->echo_control_mobile()->GetEchoPath(echo_path, path_size);
+    ASSERT_EQ(path_size, fwrite(echo_path,
+                                sizeof(unsigned char),
+                                path_size,
+                                aecm_echo_path_out_file));
+    fclose(aecm_echo_path_out_file);
+    aecm_echo_path_out_file = NULL;
+  }
+
   if (verbose) {
     printf("\nProcessed frames: %d (primary), %d (reverse)\n",
         primary_count, reverse_count);
diff --git a/src/modules/audio_processing/main/test/unit_test/unit_test.cc b/src/modules/audio_processing/main/test/unit_test/unit_test.cc
index 3a6fce5..275ac3f 100644
--- a/src/modules/audio_processing/main/test/unit_test/unit_test.cc
+++ b/src/modules/audio_processing/main/test/unit_test/unit_test.cc
@@ -638,6 +638,31 @@
   EXPECT_EQ(apm_->kNoError,
       apm_->echo_control_mobile()->enable_comfort_noise(true));
   EXPECT_TRUE(apm_->echo_control_mobile()->is_comfort_noise_enabled());
+  // Set and get echo path
+  const int echo_path_size = apm_->echo_control_mobile()->echo_path_size_bytes();
+  unsigned char echo_path_in[echo_path_size];
+  unsigned char echo_path_out[echo_path_size];
+  EXPECT_EQ(apm_->kNullPointerError,
+            apm_->echo_control_mobile()->SetEchoPath(NULL, echo_path_size));
+  EXPECT_EQ(apm_->kNullPointerError,
+            apm_->echo_control_mobile()->GetEchoPath(NULL, echo_path_size));
+  EXPECT_EQ(apm_->kBadParameterError,
+            apm_->echo_control_mobile()->GetEchoPath(echo_path_out, 1));
+  EXPECT_EQ(apm_->kNoError,
+            apm_->echo_control_mobile()->GetEchoPath(echo_path_out,
+                                                     echo_path_size));
+  for (int i = 0; i < echo_path_size; i++) {
+    echo_path_in[i] = echo_path_out[i] + 1;
+  }
+  EXPECT_EQ(apm_->kBadParameterError,
+            apm_->echo_control_mobile()->SetEchoPath(echo_path_in, 1));
+  EXPECT_EQ(apm_->kNoError,
+            apm_->echo_control_mobile()->SetEchoPath(echo_path_in, echo_path_size));
+  EXPECT_EQ(apm_->kNoError,
+            apm_->echo_control_mobile()->GetEchoPath(echo_path_out, echo_path_size));
+  for (int i = 0; i < echo_path_size; i++) {
+    EXPECT_EQ(echo_path_in[i], echo_path_out[i]);
+  }
   // Turn AECM off
   EXPECT_EQ(apm_->kNoError, apm_->echo_control_mobile()->Enable(false));
   EXPECT_FALSE(apm_->echo_control_mobile()->is_enabled());