Expose ILBC codec in webrtc/api/audio_codecs/

BUG=webrtc:7834, webrtc:7840

Review-Url: https://codereview.webrtc.org/2951873002
Cr-Commit-Position: refs/heads/master@{#18803}
diff --git a/webrtc/api/audio_codecs/ilbc/BUILD.gn b/webrtc/api/audio_codecs/ilbc/BUILD.gn
new file mode 100644
index 0000000..bba2662
--- /dev/null
+++ b/webrtc/api/audio_codecs/ilbc/BUILD.gn
@@ -0,0 +1,45 @@
+# Copyright (c) 2017 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.
+
+import("../../../webrtc.gni")
+if (is_android) {
+  import("//build/config/android/config.gni")
+  import("//build/config/android/rules.gni")
+}
+
+rtc_source_set("audio_encoder_ilbc_config") {
+  sources = [
+    "audio_encoder_ilbc_config.h",
+  ]
+}
+
+rtc_static_library("audio_encoder_ilbc") {
+  sources = [
+    "audio_encoder_ilbc.cc",
+    "audio_encoder_ilbc.h",
+  ]
+  deps = [
+    ":audio_encoder_ilbc_config",
+    "..:audio_codecs_api",
+    "../../../base:rtc_base_approved",
+    "../../../modules/audio_coding:ilbc",
+  ]
+}
+
+rtc_static_library("audio_decoder_ilbc") {
+  sources = [
+    "audio_decoder_ilbc.cc",
+    "audio_decoder_ilbc.h",
+  ]
+  deps = [
+    "..:audio_codecs_api",
+    "../../..:webrtc_common",
+    "../../../base:rtc_base_approved",
+    "../../../modules/audio_coding:ilbc",
+  ]
+}
diff --git a/webrtc/api/audio_codecs/ilbc/audio_decoder_ilbc.cc b/webrtc/api/audio_codecs/ilbc/audio_decoder_ilbc.cc
new file mode 100644
index 0000000..da290af
--- /dev/null
+++ b/webrtc/api/audio_codecs/ilbc/audio_decoder_ilbc.cc
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (c) 2017 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 "webrtc/api/audio_codecs/ilbc/audio_decoder_ilbc.h"
+
+#include <memory>
+#include <vector>
+
+#include "webrtc/base/ptr_util.h"
+#include "webrtc/common_types.h"
+#include "webrtc/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.h"
+
+namespace webrtc {
+
+rtc::Optional<AudioDecoderIlbc::Config> AudioDecoderIlbc::SdpToConfig(
+    const SdpAudioFormat& format) {
+  return STR_CASE_CMP(format.name.c_str(), "ilbc") == 0 &&
+                 format.clockrate_hz == 8000 && format.num_channels == 1
+             ? rtc::Optional<Config>(Config())
+             : rtc::Optional<Config>();
+}
+
+void AudioDecoderIlbc::AppendSupportedDecoders(
+    std::vector<AudioCodecSpec>* specs) {
+  specs->push_back({{"ILBC", 8000, 1}, {8000, 1, 13300}});
+}
+
+std::unique_ptr<AudioDecoder> AudioDecoderIlbc::MakeAudioDecoder(
+    Config config) {
+  return rtc::MakeUnique<AudioDecoderIlbcImpl>();
+}
+
+}  // namespace webrtc
diff --git a/webrtc/api/audio_codecs/ilbc/audio_decoder_ilbc.h b/webrtc/api/audio_codecs/ilbc/audio_decoder_ilbc.h
new file mode 100644
index 0000000..1d58810
--- /dev/null
+++ b/webrtc/api/audio_codecs/ilbc/audio_decoder_ilbc.h
@@ -0,0 +1,36 @@
+/*
+ *  Copyright (c) 2017 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.
+ */
+
+#ifndef WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_DECODER_ILBC_H_
+#define WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_DECODER_ILBC_H_
+
+#include <memory>
+#include <vector>
+
+#include "webrtc/api/audio_codecs/audio_decoder.h"
+#include "webrtc/api/audio_codecs/audio_format.h"
+#include "webrtc/base/optional.h"
+
+namespace webrtc {
+
+// ILBC decoder API for use as a template parameter to
+// CreateAudioDecoderFactory<...>().
+//
+// NOTE: This struct is still under development and may change without notice.
+struct AudioDecoderIlbc {
+  struct Config {};  // Empty---no config values needed!
+  static rtc::Optional<Config> SdpToConfig(const SdpAudioFormat& audio_format);
+  static void AppendSupportedDecoders(std::vector<AudioCodecSpec>* specs);
+  static std::unique_ptr<AudioDecoder> MakeAudioDecoder(Config config);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_DECODER_ILBC_H_
diff --git a/webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc.cc b/webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc.cc
new file mode 100644
index 0000000..a4b3a38
--- /dev/null
+++ b/webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc.cc
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (c) 2017 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 "webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc.h"
+
+#include <memory>
+#include <vector>
+
+#include "webrtc/base/ptr_util.h"
+#include "webrtc/base/safe_conversions.h"
+#include "webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h"
+
+namespace webrtc {
+namespace {
+int GetIlbcBitrate(int ptime) {
+  switch (ptime) {
+    case 20:
+    case 40:
+      // 38 bytes per frame of 20 ms => 15200 bits/s.
+      return 15200;
+    case 30:
+    case 60:
+      // 50 bytes per frame of 30 ms => (approx) 13333 bits/s.
+      return 13333;
+    default:
+      FATAL();
+  }
+}
+}  // namespace
+
+rtc::Optional<AudioEncoderIlbcConfig> AudioEncoderIlbc::SdpToConfig(
+    const SdpAudioFormat& format) {
+  return AudioEncoderIlbcImpl::SdpToConfig(format);
+}
+
+void AudioEncoderIlbc::AppendSupportedEncoders(
+    std::vector<AudioCodecSpec>* specs) {
+  const SdpAudioFormat fmt = {"ILBC", 8000, 1};
+  const AudioCodecInfo info = QueryAudioEncoder(*SdpToConfig(fmt));
+  specs->push_back({fmt, info});
+}
+
+AudioCodecInfo AudioEncoderIlbc::QueryAudioEncoder(
+    const AudioEncoderIlbcConfig& config) {
+  RTC_DCHECK(config.IsOk());
+  return {8000, 1, GetIlbcBitrate(config.frame_size_ms)};
+}
+
+std::unique_ptr<AudioEncoder> AudioEncoderIlbc::MakeAudioEncoder(
+    const AudioEncoderIlbcConfig& config,
+    int payload_type) {
+  RTC_DCHECK(config.IsOk());
+  return rtc::MakeUnique<AudioEncoderIlbcImpl>(config, payload_type);
+}
+
+}  // namespace webrtc
diff --git a/webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc.h b/webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc.h
new file mode 100644
index 0000000..4201035
--- /dev/null
+++ b/webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc.h
@@ -0,0 +1,40 @@
+/*
+ *  Copyright (c) 2017 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.
+ */
+
+#ifndef WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_ENCODER_ILBC_H_
+#define WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_ENCODER_ILBC_H_
+
+#include <memory>
+#include <vector>
+
+#include "webrtc/api/audio_codecs/audio_encoder.h"
+#include "webrtc/api/audio_codecs/audio_format.h"
+#include "webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_config.h"
+#include "webrtc/base/optional.h"
+
+namespace webrtc {
+
+// ILBC encoder API for use as a template parameter to
+// CreateAudioEncoderFactory<...>().
+//
+// NOTE: This struct is still under development and may change without notice.
+struct AudioEncoderIlbc {
+  static rtc::Optional<AudioEncoderIlbcConfig> SdpToConfig(
+      const SdpAudioFormat& audio_format);
+  static void AppendSupportedEncoders(std::vector<AudioCodecSpec>* specs);
+  static AudioCodecInfo QueryAudioEncoder(const AudioEncoderIlbcConfig& config);
+  static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
+      const AudioEncoderIlbcConfig& config,
+      int payload_type);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_ENCODER_ILBC_H_
diff --git a/webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_config.h b/webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_config.h
new file mode 100644
index 0000000..429ac81
--- /dev/null
+++ b/webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_config.h
@@ -0,0 +1,29 @@
+/*
+ *  Copyright (c) 2017 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.
+ */
+
+#ifndef WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_ENCODER_ILBC_CONFIG_H_
+#define WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_ENCODER_ILBC_CONFIG_H_
+
+namespace webrtc {
+
+// NOTE: This struct is still under development and may change without notice.
+struct AudioEncoderIlbcConfig {
+  bool IsOk() const {
+    return (frame_size_ms == 20 || frame_size_ms == 30 || frame_size_ms == 40 ||
+            frame_size_ms == 60);
+  }
+  int frame_size_ms = 30;  // Valid values are 20, 30, 40, and 60 ms.
+  // Note that frame size 40 ms produces encodings with two 20 ms frames in
+  // them, and frame size 60 ms consists of two 30 ms frames.
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_API_AUDIO_CODECS_ILBC_AUDIO_ENCODER_ILBC_CONFIG_H_
diff --git a/webrtc/api/audio_codecs/test/BUILD.gn b/webrtc/api/audio_codecs/test/BUILD.gn
index 08d527d..38ca736 100644
--- a/webrtc/api/audio_codecs/test/BUILD.gn
+++ b/webrtc/api/audio_codecs/test/BUILD.gn
@@ -26,6 +26,8 @@
       "../../../test:test_support",
       "../g722:audio_decoder_g722",
       "../g722:audio_encoder_g722",
+      "../ilbc:audio_decoder_ilbc",
+      "../ilbc:audio_encoder_ilbc",
       "//testing/gmock",
     ]
   }
diff --git a/webrtc/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc b/webrtc/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc
index c05fcd6..283a5f5 100644
--- a/webrtc/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc
+++ b/webrtc/api/audio_codecs/test/audio_decoder_factory_template_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "webrtc/api/audio_codecs/audio_decoder_factory_template.h"
 #include "webrtc/api/audio_codecs/g722/audio_decoder_g722.h"
+#include "webrtc/api/audio_codecs/ilbc/audio_decoder_ilbc.h"
 #include "webrtc/base/ptr_util.h"
 #include "webrtc/test/gmock.h"
 #include "webrtc/test/gtest.h"
@@ -131,4 +132,17 @@
   ASSERT_EQ(nullptr, dec3);
 }
 
+TEST(AudioDecoderFactoryTemplateTest, Ilbc) {
+  auto factory = CreateAudioDecoderFactory<AudioDecoderIlbc>();
+  EXPECT_THAT(factory->GetSupportedDecoders(),
+              testing::ElementsAre(
+                  AudioCodecSpec{{"ILBC", 8000, 1}, {8000, 1, 13300}}));
+  EXPECT_FALSE(factory->IsSupportedDecoder({"foo", 8000, 1}));
+  EXPECT_TRUE(factory->IsSupportedDecoder({"ilbc", 8000, 1}));
+  EXPECT_EQ(nullptr, factory->MakeAudioDecoder({"bar", 8000, 1}));
+  auto dec = factory->MakeAudioDecoder({"ilbc", 8000, 1});
+  ASSERT_NE(nullptr, dec);
+  EXPECT_EQ(8000, dec->SampleRateHz());
+}
+
 }  // namespace webrtc
diff --git a/webrtc/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc b/webrtc/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc
index abca968..c3c07c6 100644
--- a/webrtc/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc
+++ b/webrtc/api/audio_codecs/test/audio_encoder_factory_template_unittest.cc
@@ -10,6 +10,7 @@
 
 #include "webrtc/api/audio_codecs/audio_encoder_factory_template.h"
 #include "webrtc/api/audio_codecs/g722/audio_encoder_g722.h"
+#include "webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc.h"
 #include "webrtc/base/ptr_util.h"
 #include "webrtc/test/gmock.h"
 #include "webrtc/test/gtest.h"
@@ -133,4 +134,19 @@
   EXPECT_EQ(16000, enc->SampleRateHz());
 }
 
+TEST(AudioEncoderFactoryTemplateTest, Ilbc) {
+  auto factory = CreateAudioEncoderFactory<AudioEncoderIlbc>();
+  EXPECT_THAT(factory->GetSupportedEncoders(),
+              testing::ElementsAre(
+                  AudioCodecSpec{{"ILBC", 8000, 1}, {8000, 1, 13333}}));
+  EXPECT_EQ(rtc::Optional<AudioCodecInfo>(),
+            factory->QueryAudioEncoder({"foo", 8000, 1}));
+  EXPECT_EQ(rtc::Optional<AudioCodecInfo>({8000, 1, 13333}),
+            factory->QueryAudioEncoder({"ilbc", 8000, 1}));
+  EXPECT_EQ(nullptr, factory->MakeAudioEncoder(17, {"bar", 8000, 1}));
+  auto enc = factory->MakeAudioEncoder(17, {"ilbc", 8000, 1});
+  ASSERT_NE(nullptr, enc);
+  EXPECT_EQ(8000, enc->SampleRateHz());
+}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn
index afd2720..30460ba 100644
--- a/webrtc/modules/audio_coding/BUILD.gn
+++ b/webrtc/modules/audio_coding/BUILD.gn
@@ -322,6 +322,7 @@
     ":legacy_encoded_audio_frame",
     "../..:webrtc_common",
     "../../api/audio_codecs:audio_codecs_api",
+    "../../api/audio_codecs/ilbc:audio_encoder_ilbc_config",
     "../../base:rtc_base_approved",
     "../../common_audio",
   ]
diff --git a/webrtc/modules/audio_coding/acm2/rent_a_codec.cc b/webrtc/modules/audio_coding/acm2/rent_a_codec.cc
index 79491b8..8456285 100644
--- a/webrtc/modules/audio_coding/acm2/rent_a_codec.cc
+++ b/webrtc/modules/audio_coding/acm2/rent_a_codec.cc
@@ -173,7 +173,7 @@
     return std::unique_ptr<AudioEncoder>(new AudioEncoderPcm16B(speech_inst));
 #ifdef WEBRTC_CODEC_ILBC
   if (STR_CASE_CMP(speech_inst.plname, "ilbc") == 0)
-    return std::unique_ptr<AudioEncoder>(new AudioEncoderIlbc(speech_inst));
+    return std::unique_ptr<AudioEncoder>(new AudioEncoderIlbcImpl(speech_inst));
 #endif
 #ifdef WEBRTC_CODEC_G722
   if (STR_CASE_CMP(speech_inst.plname, "g722") == 0)
diff --git a/webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory_internal.cc b/webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory_internal.cc
index 332ad06..ce7ec3d 100644
--- a/webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory_internal.cc
+++ b/webrtc/modules/audio_coding/codecs/builtin_audio_decoder_factory_internal.cc
@@ -78,7 +78,7 @@
      [](const SdpAudioFormat& format, std::unique_ptr<AudioDecoder>* out) {
        if (format.clockrate_hz == 8000 && format.num_channels == 1) {
          if (out) {
-           out->reset(new AudioDecoderIlbc);
+           out->reset(new AudioDecoderIlbcImpl);
          }
          return true;
        } else {
diff --git a/webrtc/modules/audio_coding/codecs/builtin_audio_encoder_factory_internal.cc b/webrtc/modules/audio_coding/codecs/builtin_audio_encoder_factory_internal.cc
index 3f75cc8..3c8c030 100644
--- a/webrtc/modules/audio_coding/codecs/builtin_audio_encoder_factory_internal.cc
+++ b/webrtc/modules/audio_coding/codecs/builtin_audio_encoder_factory_internal.cc
@@ -65,7 +65,7 @@
     NamedEncoderFactory::ForEncoder<AudioEncoderG722Impl>(),
 #endif
 #ifdef WEBRTC_CODEC_ILBC
-    NamedEncoderFactory::ForEncoder<AudioEncoderIlbc>(),
+    NamedEncoderFactory::ForEncoder<AudioEncoderIlbcImpl>(),
 #endif
 #if defined(WEBRTC_CODEC_ISACFX)
     NamedEncoderFactory::ForEncoder<AudioEncoderIsacFix>(),
diff --git a/webrtc/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.cc b/webrtc/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.cc
index a0fc02b..a95c7d5 100644
--- a/webrtc/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.cc
+++ b/webrtc/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.cc
@@ -19,20 +19,20 @@
 
 namespace webrtc {
 
-AudioDecoderIlbc::AudioDecoderIlbc() {
+AudioDecoderIlbcImpl::AudioDecoderIlbcImpl() {
   WebRtcIlbcfix_DecoderCreate(&dec_state_);
   WebRtcIlbcfix_Decoderinit30Ms(dec_state_);
 }
 
-AudioDecoderIlbc::~AudioDecoderIlbc() {
+AudioDecoderIlbcImpl::~AudioDecoderIlbcImpl() {
   WebRtcIlbcfix_DecoderFree(dec_state_);
 }
 
-bool AudioDecoderIlbc::HasDecodePlc() const {
+bool AudioDecoderIlbcImpl::HasDecodePlc() const {
   return true;
 }
 
-int AudioDecoderIlbc::DecodeInternal(const uint8_t* encoded,
+int AudioDecoderIlbcImpl::DecodeInternal(const uint8_t* encoded,
                                      size_t encoded_len,
                                      int sample_rate_hz,
                                      int16_t* decoded,
@@ -45,22 +45,22 @@
   return ret;
 }
 
-size_t AudioDecoderIlbc::DecodePlc(size_t num_frames, int16_t* decoded) {
+size_t AudioDecoderIlbcImpl::DecodePlc(size_t num_frames, int16_t* decoded) {
   return WebRtcIlbcfix_NetEqPlc(dec_state_, decoded, num_frames);
 }
 
-void AudioDecoderIlbc::Reset() {
+void AudioDecoderIlbcImpl::Reset() {
   WebRtcIlbcfix_Decoderinit30Ms(dec_state_);
 }
 
-std::vector<AudioDecoder::ParseResult> AudioDecoderIlbc::ParsePayload(
+std::vector<AudioDecoder::ParseResult> AudioDecoderIlbcImpl::ParsePayload(
     rtc::Buffer&& payload,
     uint32_t timestamp) {
   std::vector<ParseResult> results;
   size_t bytes_per_frame;
   int timestamps_per_frame;
   if (payload.size() >= 950) {
-    LOG(LS_WARNING) << "AudioDecoderIlbc::ParsePayload: Payload too large";
+    LOG(LS_WARNING) << "AudioDecoderIlbcImpl::ParsePayload: Payload too large";
     return results;
   }
   if (payload.size() % 38 == 0) {
@@ -72,7 +72,7 @@
     bytes_per_frame = 50;
     timestamps_per_frame = 240;
   } else {
-    LOG(LS_WARNING) << "AudioDecoderIlbc::ParsePayload: Invalid payload";
+    LOG(LS_WARNING) << "AudioDecoderIlbcImpl::ParsePayload: Invalid payload";
     return results;
   }
 
@@ -97,11 +97,11 @@
   return results;
 }
 
-int AudioDecoderIlbc::SampleRateHz() const {
+int AudioDecoderIlbcImpl::SampleRateHz() const {
   return 8000;
 }
 
-size_t AudioDecoderIlbc::Channels() const {
+size_t AudioDecoderIlbcImpl::Channels() const {
   return 1;
 }
 
diff --git a/webrtc/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.h b/webrtc/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.h
index bc01a5d..5269f4d 100644
--- a/webrtc/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.h
+++ b/webrtc/modules/audio_coding/codecs/ilbc/audio_decoder_ilbc.h
@@ -18,10 +18,10 @@
 
 namespace webrtc {
 
-class AudioDecoderIlbc final : public AudioDecoder {
+class AudioDecoderIlbcImpl final : public AudioDecoder {
  public:
-  AudioDecoderIlbc();
-  ~AudioDecoderIlbc() override;
+  AudioDecoderIlbcImpl();
+  ~AudioDecoderIlbcImpl() override;
   bool HasDecodePlc() const override;
   size_t DecodePlc(size_t num_frames, int16_t* decoded) override;
   void Reset() override;
@@ -39,7 +39,7 @@
 
  private:
   IlbcDecoderInstance* dec_state_;
-  RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderIlbc);
+  RTC_DISALLOW_COPY_AND_ASSIGN(AudioDecoderIlbcImpl);
 };
 
 }  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.cc b/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.cc
index 6ed445a..0dc9bb4 100644
--- a/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.cc
+++ b/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.cc
@@ -24,34 +24,20 @@
 
 const int kSampleRateHz = 8000;
 
-AudioEncoderIlbc::Config CreateConfig(const CodecInst& codec_inst) {
-  AudioEncoderIlbc::Config config;
+AudioEncoderIlbcConfig CreateConfig(const CodecInst& codec_inst) {
+  AudioEncoderIlbcConfig config;
   config.frame_size_ms = codec_inst.pacsize / 8;
-  config.payload_type = codec_inst.pltype;
-  return config;
-}
-
-AudioEncoderIlbc::Config CreateConfig(int payload_type,
-                                      const SdpAudioFormat& format) {
-  AudioEncoderIlbc::Config config;
-  config.payload_type = payload_type;
-  auto ptime_iter = format.parameters.find("ptime");
-  if (ptime_iter != format.parameters.end()) {
-    auto ptime = rtc::StringToNumber<int>(ptime_iter->second);
-    if (ptime && *ptime > 0) {
-      const int whole_packets = *ptime / 10;
-      config.frame_size_ms = std::max(20, std::min(whole_packets * 10, 60));
-    }
-  }
   return config;
 }
 
 int GetIlbcBitrate(int ptime) {
   switch (ptime) {
-    case 20: case 40:
+    case 20:
+    case 40:
       // 38 bytes per frame of 20 ms => 15200 bits/s.
       return 15200;
-    case 30: case 60:
+    case 30:
+    case 60:
       // 50 bytes per frame of 30 ms => (approx) 13333 bits/s.
       return 13333;
     default:
@@ -61,71 +47,85 @@
 
 }  // namespace
 
-// static
-const size_t AudioEncoderIlbc::kMaxSamplesPerPacket;
+rtc::Optional<AudioEncoderIlbcConfig> AudioEncoderIlbcImpl::SdpToConfig(
+    const SdpAudioFormat& format) {
+  if (STR_CASE_CMP(format.name.c_str(), "ilbc") != 0 ||
+      format.clockrate_hz != 8000 || format.num_channels != 1) {
+    return rtc::Optional<AudioEncoderIlbcConfig>();
+  }
 
-bool AudioEncoderIlbc::Config::IsOk() const {
-  return (frame_size_ms == 20 || frame_size_ms == 30 || frame_size_ms == 40 ||
-          frame_size_ms == 60) &&
-      static_cast<size_t>(kSampleRateHz / 100 * (frame_size_ms / 10)) <=
-          kMaxSamplesPerPacket;
+  AudioEncoderIlbcConfig config;
+  auto ptime_iter = format.parameters.find("ptime");
+  if (ptime_iter != format.parameters.end()) {
+    auto ptime = rtc::StringToNumber<int>(ptime_iter->second);
+    if (ptime && *ptime > 0) {
+      const int whole_packets = *ptime / 10;
+      config.frame_size_ms = std::max(20, std::min(whole_packets * 10, 60));
+    }
+  }
+  return config.IsOk() ? rtc::Optional<AudioEncoderIlbcConfig>(config)
+                       : rtc::Optional<AudioEncoderIlbcConfig>();
 }
 
-AudioEncoderIlbc::AudioEncoderIlbc(const Config& config)
-    : config_(config),
+AudioEncoderIlbcImpl::AudioEncoderIlbcImpl(const AudioEncoderIlbcConfig& config,
+                                           int payload_type)
+    : frame_size_ms_(config.frame_size_ms),
+      payload_type_(payload_type),
       num_10ms_frames_per_packet_(
           static_cast<size_t>(config.frame_size_ms / 10)),
       encoder_(nullptr) {
+  RTC_CHECK(config.IsOk());
   Reset();
 }
 
-AudioEncoderIlbc::AudioEncoderIlbc(const CodecInst& codec_inst)
-    : AudioEncoderIlbc(CreateConfig(codec_inst)) {}
+AudioEncoderIlbcImpl::AudioEncoderIlbcImpl(const CodecInst& codec_inst)
+    : AudioEncoderIlbcImpl(CreateConfig(codec_inst), codec_inst.pltype) {}
 
-AudioEncoderIlbc::AudioEncoderIlbc(int payload_type,
-                                   const SdpAudioFormat& format)
-    : AudioEncoderIlbc(CreateConfig(payload_type, format)) {}
+AudioEncoderIlbcImpl::AudioEncoderIlbcImpl(int payload_type,
+                                           const SdpAudioFormat& format)
+    : AudioEncoderIlbcImpl(*SdpToConfig(format), payload_type) {}
 
-rtc::Optional<AudioCodecInfo> AudioEncoderIlbc::QueryAudioEncoder(
-    const SdpAudioFormat& format) {
-  if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0 &&
-      format.clockrate_hz == 8000 && format.num_channels == 1) {
-    Config config = CreateConfig(0, format);
-    if (config.IsOk()) {
-      return rtc::Optional<AudioCodecInfo>(
-          {kSampleRateHz, 1, GetIlbcBitrate(config.frame_size_ms)});
-    }
-  }
-
-  return rtc::Optional<AudioCodecInfo>();
-}
-
-AudioEncoderIlbc::~AudioEncoderIlbc() {
+AudioEncoderIlbcImpl::~AudioEncoderIlbcImpl() {
   RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderFree(encoder_));
 }
 
-int AudioEncoderIlbc::SampleRateHz() const {
+rtc::Optional<AudioCodecInfo> AudioEncoderIlbcImpl::QueryAudioEncoder(
+    const SdpAudioFormat& format) {
+  if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0) {
+    const auto config_opt = SdpToConfig(format);
+    if (format.clockrate_hz == 8000 && format.num_channels == 1 &&
+        config_opt) {
+      RTC_DCHECK(config_opt->IsOk());
+      return rtc::Optional<AudioCodecInfo>(
+          {rtc::dchecked_cast<int>(kSampleRateHz), 1,
+           GetIlbcBitrate(config_opt->frame_size_ms)});
+    }
+  }
+  return rtc::Optional<AudioCodecInfo>();
+}
+
+int AudioEncoderIlbcImpl::SampleRateHz() const {
   return kSampleRateHz;
 }
 
-size_t AudioEncoderIlbc::NumChannels() const {
+size_t AudioEncoderIlbcImpl::NumChannels() const {
   return 1;
 }
 
-size_t AudioEncoderIlbc::Num10MsFramesInNextPacket() const {
+size_t AudioEncoderIlbcImpl::Num10MsFramesInNextPacket() const {
   return num_10ms_frames_per_packet_;
 }
 
-size_t AudioEncoderIlbc::Max10MsFramesInAPacket() const {
+size_t AudioEncoderIlbcImpl::Max10MsFramesInAPacket() const {
   return num_10ms_frames_per_packet_;
 }
 
-int AudioEncoderIlbc::GetTargetBitrate() const {
+int AudioEncoderIlbcImpl::GetTargetBitrate() const {
   return GetIlbcBitrate(rtc::dchecked_cast<int>(num_10ms_frames_per_packet_) *
                         10);
 }
 
-AudioEncoder::EncodedInfo AudioEncoderIlbc::EncodeImpl(
+AudioEncoder::EncodedInfo AudioEncoderIlbcImpl::EncodeImpl(
     uint32_t rtp_timestamp,
     rtc::ArrayView<const int16_t> audio,
     rtc::Buffer* encoded) {
@@ -166,24 +166,23 @@
   EncodedInfo info;
   info.encoded_bytes = encoded_bytes;
   info.encoded_timestamp = first_timestamp_in_buffer_;
-  info.payload_type = config_.payload_type;
+  info.payload_type = payload_type_;
   info.encoder_type = CodecType::kIlbc;
   return info;
 }
 
-void AudioEncoderIlbc::Reset() {
+void AudioEncoderIlbcImpl::Reset() {
   if (encoder_)
     RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderFree(encoder_));
-  RTC_CHECK(config_.IsOk());
   RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderCreate(&encoder_));
-  const int encoder_frame_size_ms = config_.frame_size_ms > 30
-                                        ? config_.frame_size_ms / 2
-                                        : config_.frame_size_ms;
+  const int encoder_frame_size_ms = frame_size_ms_ > 30
+                                        ? frame_size_ms_ / 2
+                                        : frame_size_ms_;
   RTC_CHECK_EQ(0, WebRtcIlbcfix_EncoderInit(encoder_, encoder_frame_size_ms));
   num_10ms_frames_buffered_ = 0;
 }
 
-size_t AudioEncoderIlbc::RequiredOutputSizeBytes() const {
+size_t AudioEncoderIlbcImpl::RequiredOutputSizeBytes() const {
   switch (num_10ms_frames_per_packet_) {
     case 2:   return 38;
     case 3:   return 50;
diff --git a/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h b/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h
index 6d3d102..0d9b366 100644
--- a/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h
+++ b/webrtc/modules/audio_coding/codecs/ilbc/audio_encoder_ilbc.h
@@ -13,6 +13,7 @@
 
 #include "webrtc/api/audio_codecs/audio_encoder.h"
 #include "webrtc/api/audio_codecs/audio_format.h"
+#include "webrtc/api/audio_codecs/ilbc/audio_encoder_ilbc_config.h"
 #include "webrtc/base/constructormagic.h"
 #include "webrtc/modules/audio_coding/codecs/ilbc/ilbc.h"
 
@@ -20,21 +21,15 @@
 
 struct CodecInst;
 
-class AudioEncoderIlbc final : public AudioEncoder {
+class AudioEncoderIlbcImpl final : public AudioEncoder {
  public:
-  struct Config {
-    bool IsOk() const;
+  static rtc::Optional<AudioEncoderIlbcConfig> SdpToConfig(
+      const SdpAudioFormat& format);
 
-    int payload_type = 102;
-    int frame_size_ms = 30;  // Valid values are 20, 30, 40, and 60 ms.
-    // Note that frame size 40 ms produces encodings with two 20 ms frames in
-    // them, and frame size 60 ms consists of two 30 ms frames.
-  };
-
-  explicit AudioEncoderIlbc(const Config& config);
-  explicit AudioEncoderIlbc(const CodecInst& codec_inst);
-  AudioEncoderIlbc(int payload_type, const SdpAudioFormat& format);
-  ~AudioEncoderIlbc() override;
+  AudioEncoderIlbcImpl(const AudioEncoderIlbcConfig& config, int payload_type);
+  explicit AudioEncoderIlbcImpl(const CodecInst& codec_inst);
+  AudioEncoderIlbcImpl(int payload_type, const SdpAudioFormat& format);
+  ~AudioEncoderIlbcImpl() override;
 
   static constexpr const char* GetPayloadName() { return "ILBC"; }
   static rtc::Optional<AudioCodecInfo> QueryAudioEncoder(
@@ -53,14 +48,15 @@
  private:
   size_t RequiredOutputSizeBytes() const;
 
-  static const size_t kMaxSamplesPerPacket = 480;
-  const Config config_;
+  static constexpr size_t kMaxSamplesPerPacket = 480;
+  const int frame_size_ms_;
+  const int payload_type_;
   const size_t num_10ms_frames_per_packet_;
   size_t num_10ms_frames_buffered_;
   uint32_t first_timestamp_in_buffer_;
   int16_t input_buffer_[kMaxSamplesPerPacket];
   IlbcEncoderInstance* encoder_;
-  RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderIlbc);
+  RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderIlbcImpl);
 };
 
 }  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/codecs/ilbc/ilbc_unittest.cc b/webrtc/modules/audio_coding/codecs/ilbc/ilbc_unittest.cc
index dbd5c07..69e2181 100644
--- a/webrtc/modules/audio_coding/codecs/ilbc/ilbc_unittest.cc
+++ b/webrtc/modules/audio_coding/codecs/ilbc/ilbc_unittest.cc
@@ -17,11 +17,11 @@
 
 TEST(IlbcTest, BadPacket) {
   // Get a good packet.
-  AudioEncoderIlbc::Config config;
+  AudioEncoderIlbcConfig config;
   config.frame_size_ms = 20;  // We need 20 ms rather than the default 30 ms;
                               // otherwise, all possible values of cb_index[2]
                               // are valid.
-  AudioEncoderIlbc encoder(config);
+  AudioEncoderIlbcImpl encoder(config, 102);
   std::vector<int16_t> samples(encoder.SampleRateHz() / 100, 4711);
   rtc::Buffer packet;
   int num_10ms_chunks = 0;
@@ -39,7 +39,7 @@
   bad_packet[30] |= 0x80;  // Bit 0.
 
   // Decode the bad packet. We expect the decoder to respond by returning -1.
-  AudioDecoderIlbc decoder;
+  AudioDecoderIlbcImpl decoder;
   std::vector<int16_t> decoded_samples(num_10ms_chunks * samples.size());
   AudioDecoder::SpeechType speech_type;
   EXPECT_EQ(-1, decoder.Decode(bad_packet.data(), bad_packet.size(),
@@ -69,7 +69,7 @@
 };
 
 TEST_P(SplitIlbcTest, NumFrames) {
-  AudioDecoderIlbc decoder;
+  AudioDecoderIlbcImpl decoder;
   const size_t frame_length_samples = frame_length_ms_ * 8;
   const auto generate_payload = [] (size_t payload_length_bytes) {
     rtc::Buffer payload(payload_length_bytes);
@@ -120,7 +120,7 @@
 
 // Test too large payload size.
 TEST(IlbcTest, SplitTooLargePayload) {
-  AudioDecoderIlbc decoder;
+  AudioDecoderIlbcImpl decoder;
   constexpr size_t kPayloadLengthBytes = 950;
   const auto results =
       decoder.ParsePayload(rtc::Buffer(kPayloadLengthBytes), 0);
@@ -129,7 +129,7 @@
 
 // Payload not an integer number of frames.
 TEST(IlbcTest, SplitUnevenPayload) {
-  AudioDecoderIlbc decoder;
+  AudioDecoderIlbcImpl decoder;
   constexpr size_t kPayloadLengthBytes = 39;  // Not an even number of frames.
   const auto results =
       decoder.ParsePayload(rtc::Buffer(kPayloadLengthBytes), 0);
diff --git a/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc b/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc
index 6d71b85..15a89e9 100644
--- a/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc
+++ b/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc
@@ -315,12 +315,11 @@
     codec_input_rate_hz_ = 8000;
     frame_size_ = 240;
     data_length_ = 10 * frame_size_;
-    decoder_ = new AudioDecoderIlbc;
+    decoder_ = new AudioDecoderIlbcImpl;
     assert(decoder_);
-    AudioEncoderIlbc::Config config;
+    AudioEncoderIlbcConfig config;
     config.frame_size_ms = 30;
-    config.payload_type = payload_type_;
-    audio_encoder_.reset(new AudioEncoderIlbc(config));
+    audio_encoder_.reset(new AudioEncoderIlbcImpl(config, payload_type_));
   }
 
   // Overload the default test since iLBC's function WebRtcIlbcfix_NetEqPlc does
diff --git a/webrtc/modules/audio_coding/neteq/test/neteq_ilbc_quality_test.cc b/webrtc/modules/audio_coding/neteq/test/neteq_ilbc_quality_test.cc
index 1c6d2ca..b3524a3 100644
--- a/webrtc/modules/audio_coding/neteq/test/neteq_ilbc_quality_test.cc
+++ b/webrtc/modules/audio_coding/neteq/test/neteq_ilbc_quality_test.cc
@@ -51,9 +51,9 @@
 
   void SetUp() override {
     ASSERT_EQ(1u, channels_) << "iLBC supports only mono audio.";
-    AudioEncoderIlbc::Config config;
+    AudioEncoderIlbcConfig config;
     config.frame_size_ms = FLAGS_frame_size_ms;
-    encoder_.reset(new AudioEncoderIlbc(config));
+    encoder_.reset(new AudioEncoderIlbcImpl(config, 102));
     NetEqQualityTest::SetUp();
   }
 
@@ -75,7 +75,7 @@
   }
 
  private:
-  std::unique_ptr<AudioEncoderIlbc> encoder_;
+  std::unique_ptr<AudioEncoderIlbcImpl> encoder_;
 };
 
 TEST_F(NetEqIlbcQualityTest, Test) {
diff --git a/webrtc/test/fuzzers/audio_decoder_ilbc_fuzzer.cc b/webrtc/test/fuzzers/audio_decoder_ilbc_fuzzer.cc
index b3bc434..fbc9d66 100644
--- a/webrtc/test/fuzzers/audio_decoder_ilbc_fuzzer.cc
+++ b/webrtc/test/fuzzers/audio_decoder_ilbc_fuzzer.cc
@@ -13,7 +13,7 @@
 
 namespace webrtc {
 void FuzzOneInput(const uint8_t* data, size_t size) {
-  AudioDecoderIlbc dec;
+  AudioDecoderIlbcImpl dec;
   static const int kSampleRateHz = 8000;
   static const size_t kAllocatedOuputSizeSamples = kSampleRateHz / 10;
   int16_t output[kAllocatedOuputSizeSamples];