G722 implementation of the AudioEncoderFactoryTemplate API

Now the templated AudioEncoderFactory can create G722 encoders!

BUG=webrtc:7833

Review-Url: https://codereview.webrtc.org/2934833002
Cr-Commit-Position: refs/heads/master@{#18644}
diff --git a/webrtc/api/audio_codecs/g722/BUILD.gn b/webrtc/api/audio_codecs/g722/BUILD.gn
index 78708f5..d2470a2 100644
--- a/webrtc/api/audio_codecs/g722/BUILD.gn
+++ b/webrtc/api/audio_codecs/g722/BUILD.gn
@@ -12,6 +12,25 @@
   import("//build/config/android/rules.gni")
 }
 
+rtc_source_set("audio_encoder_g722_config") {
+  sources = [
+    "audio_encoder_g722_config.h",
+  ]
+}
+
+rtc_static_library("audio_encoder_g722") {
+  sources = [
+    "audio_encoder_g722.cc",
+    "audio_encoder_g722.h",
+  ]
+  deps = [
+    ":audio_encoder_g722_config",
+    "..:audio_codecs_api",
+    "../../../base:rtc_base_approved",
+    "../../../modules/audio_coding:g722",
+  ]
+}
+
 rtc_static_library("audio_decoder_g722") {
   sources = [
     "audio_decoder_g722.cc",
diff --git a/webrtc/api/audio_codecs/g722/audio_encoder_g722.cc b/webrtc/api/audio_codecs/g722/audio_encoder_g722.cc
new file mode 100644
index 0000000..7f82b9c
--- /dev/null
+++ b/webrtc/api/audio_codecs/g722/audio_encoder_g722.cc
@@ -0,0 +1,48 @@
+/*
+ *  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/g722/audio_encoder_g722.h"
+
+#include <memory>
+#include <vector>
+
+#include "webrtc/base/ptr_util.h"
+#include "webrtc/base/safe_conversions.h"
+#include "webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.h"
+
+namespace webrtc {
+
+rtc::Optional<AudioEncoderG722Config> AudioEncoderG722::SdpToConfig(
+    const SdpAudioFormat& format) {
+  return AudioEncoderG722Impl::SdpToConfig(format);
+}
+
+void AudioEncoderG722::AppendSupportedEncoders(
+    std::vector<AudioCodecSpec>* specs) {
+  const SdpAudioFormat fmt = {"g722", 8000, 1};
+  const AudioCodecInfo info = QueryAudioEncoder(*SdpToConfig(fmt));
+  specs->push_back({fmt, info});
+}
+
+AudioCodecInfo AudioEncoderG722::QueryAudioEncoder(
+    const AudioEncoderG722Config& config) {
+  RTC_DCHECK(config.IsOk());
+  return {16000, rtc::dchecked_cast<size_t>(config.num_channels),
+          64000 * config.num_channels};
+}
+
+std::unique_ptr<AudioEncoder> AudioEncoderG722::MakeAudioEncoder(
+    const AudioEncoderG722Config& config,
+    int payload_type) {
+  RTC_DCHECK(config.IsOk());
+  return rtc::MakeUnique<AudioEncoderG722Impl>(config, payload_type);
+}
+
+}  // namespace webrtc
diff --git a/webrtc/api/audio_codecs/g722/audio_encoder_g722.h b/webrtc/api/audio_codecs/g722/audio_encoder_g722.h
new file mode 100644
index 0000000..369ebad
--- /dev/null
+++ b/webrtc/api/audio_codecs/g722/audio_encoder_g722.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_G722_AUDIO_ENCODER_G722_H_
+#define WEBRTC_API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_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/g722/audio_encoder_g722_config.h"
+#include "webrtc/base/optional.h"
+
+namespace webrtc {
+
+// G722 encoder API for use as a template parameter to
+// CreateAudioEncoderFactory<...>().
+//
+// NOTE: This struct is still under development and may change without notice.
+struct AudioEncoderG722 {
+  static rtc::Optional<AudioEncoderG722Config> SdpToConfig(
+      const SdpAudioFormat& audio_format);
+  static void AppendSupportedEncoders(std::vector<AudioCodecSpec>* specs);
+  static AudioCodecInfo QueryAudioEncoder(const AudioEncoderG722Config& config);
+  static std::unique_ptr<AudioEncoder> MakeAudioEncoder(
+      const AudioEncoderG722Config& config,
+      int payload_type);
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_H_
diff --git a/webrtc/api/audio_codecs/g722/audio_encoder_g722_config.h b/webrtc/api/audio_codecs/g722/audio_encoder_g722_config.h
new file mode 100644
index 0000000..fa26942
--- /dev/null
+++ b/webrtc/api/audio_codecs/g722/audio_encoder_g722_config.h
@@ -0,0 +1,27 @@
+/*
+ *  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_G722_AUDIO_ENCODER_G722_CONFIG_H_
+#define WEBRTC_API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_CONFIG_H_
+
+namespace webrtc {
+
+// NOTE: This struct is still under development and may change without notice.
+struct AudioEncoderG722Config {
+  bool IsOk() const {
+    return frame_size_ms > 0 && frame_size_ms % 10 == 0 && num_channels >= 1;
+  }
+  int frame_size_ms = 20;
+  int num_channels = 1;
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_API_AUDIO_CODECS_G722_AUDIO_ENCODER_G722_CONFIG_H_
diff --git a/webrtc/api/audio_codecs/test/BUILD.gn b/webrtc/api/audio_codecs/test/BUILD.gn
index 914acf4..08d527d 100644
--- a/webrtc/api/audio_codecs/test/BUILD.gn
+++ b/webrtc/api/audio_codecs/test/BUILD.gn
@@ -25,6 +25,7 @@
       "../../../test:audio_codec_mocks",
       "../../../test:test_support",
       "../g722:audio_decoder_g722",
+      "../g722:audio_encoder_g722",
       "//testing/gmock",
     ]
   }
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 15097b9..abca968 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
@@ -9,6 +9,7 @@
  */
 
 #include "webrtc/api/audio_codecs/audio_encoder_factory_template.h"
+#include "webrtc/api/audio_codecs/g722/audio_encoder_g722.h"
 #include "webrtc/base/ptr_util.h"
 #include "webrtc/test/gmock.h"
 #include "webrtc/test/gtest.h"
@@ -117,4 +118,19 @@
   EXPECT_EQ(16000, enc2->SampleRateHz());
 }
 
+TEST(AudioEncoderFactoryTemplateTest, G722) {
+  auto factory = CreateAudioEncoderFactory<AudioEncoderG722>();
+  EXPECT_THAT(factory->GetSupportedEncoders(),
+              testing::ElementsAre(
+                  AudioCodecSpec{{"g722", 8000, 1}, {16000, 1, 64000}}));
+  EXPECT_EQ(rtc::Optional<AudioCodecInfo>(),
+            factory->QueryAudioEncoder({"foo", 8000, 1}));
+  EXPECT_EQ(rtc::Optional<AudioCodecInfo>({16000, 1, 64000}),
+            factory->QueryAudioEncoder({"g722", 8000, 1}));
+  EXPECT_EQ(nullptr, factory->MakeAudioEncoder(17, {"bar", 16000, 1}));
+  auto enc = factory->MakeAudioEncoder(17, {"g722", 8000, 1});
+  ASSERT_NE(nullptr, enc);
+  EXPECT_EQ(16000, enc->SampleRateHz());
+}
+
 }  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/BUILD.gn b/webrtc/modules/audio_coding/BUILD.gn
index dc107b6..afd2720 100644
--- a/webrtc/modules/audio_coding/BUILD.gn
+++ b/webrtc/modules/audio_coding/BUILD.gn
@@ -279,6 +279,7 @@
     ":legacy_encoded_audio_frame",
     "../..:webrtc_common",
     "../../api/audio_codecs:audio_codecs_api",
+    "../../api/audio_codecs/g722:audio_encoder_g722_config",
     "../../base:rtc_base_approved",
   ]
   public_deps = [
diff --git a/webrtc/modules/audio_coding/acm2/rent_a_codec.cc b/webrtc/modules/audio_coding/acm2/rent_a_codec.cc
index b353b38..79491b8 100644
--- a/webrtc/modules/audio_coding/acm2/rent_a_codec.cc
+++ b/webrtc/modules/audio_coding/acm2/rent_a_codec.cc
@@ -177,7 +177,7 @@
 #endif
 #ifdef WEBRTC_CODEC_G722
   if (STR_CASE_CMP(speech_inst.plname, "g722") == 0)
-    return std::unique_ptr<AudioEncoder>(new AudioEncoderG722(speech_inst));
+    return std::unique_ptr<AudioEncoder>(new AudioEncoderG722Impl(speech_inst));
 #endif
   LOG_F(LS_ERROR) << "Could not create encoder of type " << speech_inst.plname;
   return std::unique_ptr<AudioEncoder>();
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 32d2553..3f75cc8 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
@@ -62,7 +62,7 @@
 
 NamedEncoderFactory encoder_factories[] = {
 #ifdef WEBRTC_CODEC_G722
-    NamedEncoderFactory::ForEncoder<AudioEncoderG722>(),
+    NamedEncoderFactory::ForEncoder<AudioEncoderG722Impl>(),
 #endif
 #ifdef WEBRTC_CODEC_ILBC
     NamedEncoderFactory::ForEncoder<AudioEncoderIlbc>(),
diff --git a/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.cc b/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.cc
index d5cf616..8d254b8 100644
--- a/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.cc
+++ b/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.cc
@@ -25,19 +25,24 @@
 
 const size_t kSampleRateHz = 16000;
 
-AudioEncoderG722::Config CreateConfig(const CodecInst& codec_inst) {
-  AudioEncoderG722::Config config;
-  config.num_channels = codec_inst.channels;
+AudioEncoderG722Config CreateConfig(const CodecInst& codec_inst) {
+  AudioEncoderG722Config config;
+  config.num_channels = rtc::dchecked_cast<int>(codec_inst.channels);
   config.frame_size_ms = codec_inst.pacsize / 16;
-  config.payload_type = codec_inst.pltype;
   return config;
 }
 
-AudioEncoderG722::Config CreateConfig(int payload_type,
-                                      const SdpAudioFormat& format) {
-  AudioEncoderG722::Config config;
-  config.payload_type = payload_type;
-  config.num_channels = format.num_channels;
+}  // namespace
+
+rtc::Optional<AudioEncoderG722Config> AudioEncoderG722Impl::SdpToConfig(
+    const SdpAudioFormat& format) {
+  if (STR_CASE_CMP(format.name.c_str(), "g722") != 0 ||
+      format.clockrate_hz != 8000) {
+    return rtc::Optional<AudioEncoderG722Config>();
+  }
+
+  AudioEncoderG722Config config;
+  config.num_channels = rtc::dchecked_cast<int>(format.num_channels);
   auto ptime_iter = format.parameters.find("ptime");
   if (ptime_iter != format.parameters.end()) {
     auto ptime = rtc::StringToNumber<int>(ptime_iter->second);
@@ -46,19 +51,14 @@
       config.frame_size_ms = std::max(10, std::min(whole_packets * 10, 60));
     }
   }
-  return config;
+  return config.IsOk() ? rtc::Optional<AudioEncoderG722Config>(config)
+                       : rtc::Optional<AudioEncoderG722Config>();
 }
 
-}  // namespace
-
-bool AudioEncoderG722::Config::IsOk() const {
-  return (frame_size_ms > 0) && (frame_size_ms % 10 == 0) &&
-      (num_channels >= 1);
-}
-
-AudioEncoderG722::AudioEncoderG722(const Config& config)
+AudioEncoderG722Impl::AudioEncoderG722Impl(const AudioEncoderG722Config& config,
+                                           int payload_type)
     : num_channels_(config.num_channels),
-      payload_type_(config.payload_type),
+      payload_type_(payload_type),
       num_10ms_frames_per_packet_(
           static_cast<size_t>(config.frame_size_ms / 10)),
       num_10ms_frames_buffered_(0),
@@ -75,61 +75,63 @@
   Reset();
 }
 
-AudioEncoderG722::AudioEncoderG722(const CodecInst& codec_inst)
-    : AudioEncoderG722(CreateConfig(codec_inst)) {}
+AudioEncoderG722Impl::AudioEncoderG722Impl(const CodecInst& codec_inst)
+    : AudioEncoderG722Impl(CreateConfig(codec_inst), codec_inst.pltype) {}
 
-AudioEncoderG722::AudioEncoderG722(int payload_type,
-                                   const SdpAudioFormat& format)
-    : AudioEncoderG722(CreateConfig(payload_type, format)) {}
+AudioEncoderG722Impl::AudioEncoderG722Impl(int payload_type,
+                                           const SdpAudioFormat& format)
+    : AudioEncoderG722Impl(*SdpToConfig(format), payload_type) {}
 
-AudioEncoderG722::~AudioEncoderG722() = default;
+AudioEncoderG722Impl::~AudioEncoderG722Impl() = default;
 
-rtc::Optional<AudioCodecInfo> AudioEncoderG722::QueryAudioEncoder(
+rtc::Optional<AudioCodecInfo> AudioEncoderG722Impl::QueryAudioEncoder(
     const SdpAudioFormat& format) {
   if (STR_CASE_CMP(format.name.c_str(), GetPayloadName()) == 0) {
-    Config config = CreateConfig(0, format);
-    if (format.clockrate_hz == 8000 && config.IsOk()) {
+    const auto config_opt = SdpToConfig(format);
+    if (format.clockrate_hz == 8000 && config_opt) {
+      RTC_DCHECK(config_opt->IsOk());
       return rtc::Optional<AudioCodecInfo>(
-          {rtc::dchecked_cast<int>(kSampleRateHz), config.num_channels, 64000});
+          {rtc::dchecked_cast<int>(kSampleRateHz),
+           rtc::dchecked_cast<size_t>(config_opt->num_channels), 64000});
     }
   }
   return rtc::Optional<AudioCodecInfo>();
 }
 
-int AudioEncoderG722::SampleRateHz() const {
+int AudioEncoderG722Impl::SampleRateHz() const {
   return kSampleRateHz;
 }
 
-size_t AudioEncoderG722::NumChannels() const {
+size_t AudioEncoderG722Impl::NumChannels() const {
   return num_channels_;
 }
 
-int AudioEncoderG722::RtpTimestampRateHz() const {
+int AudioEncoderG722Impl::RtpTimestampRateHz() const {
   // The RTP timestamp rate for G.722 is 8000 Hz, even though it is a 16 kHz
   // codec.
   return kSampleRateHz / 2;
 }
 
-size_t AudioEncoderG722::Num10MsFramesInNextPacket() const {
+size_t AudioEncoderG722Impl::Num10MsFramesInNextPacket() const {
   return num_10ms_frames_per_packet_;
 }
 
-size_t AudioEncoderG722::Max10MsFramesInAPacket() const {
+size_t AudioEncoderG722Impl::Max10MsFramesInAPacket() const {
   return num_10ms_frames_per_packet_;
 }
 
-int AudioEncoderG722::GetTargetBitrate() const {
+int AudioEncoderG722Impl::GetTargetBitrate() const {
   // 4 bits/sample, 16000 samples/s/channel.
   return static_cast<int>(64000 * NumChannels());
 }
 
-void AudioEncoderG722::Reset() {
+void AudioEncoderG722Impl::Reset() {
   num_10ms_frames_buffered_ = 0;
   for (size_t i = 0; i < num_channels_; ++i)
     RTC_CHECK_EQ(0, WebRtcG722_EncoderInit(encoders_[i].encoder));
 }
 
-AudioEncoder::EncodedInfo AudioEncoderG722::EncodeImpl(
+AudioEncoder::EncodedInfo AudioEncoderG722Impl::EncodeImpl(
     uint32_t rtp_timestamp,
     rtc::ArrayView<const int16_t> audio,
     rtc::Buffer* encoded) {
@@ -185,15 +187,15 @@
   return info;
 }
 
-AudioEncoderG722::EncoderState::EncoderState() {
+AudioEncoderG722Impl::EncoderState::EncoderState() {
   RTC_CHECK_EQ(0, WebRtcG722_CreateEncoder(&encoder));
 }
 
-AudioEncoderG722::EncoderState::~EncoderState() {
+AudioEncoderG722Impl::EncoderState::~EncoderState() {
   RTC_CHECK_EQ(0, WebRtcG722_FreeEncoder(encoder));
 }
 
-size_t AudioEncoderG722::SamplesPerChannel() const {
+size_t AudioEncoderG722Impl::SamplesPerChannel() const {
   return kSampleRateHz / 100 * num_10ms_frames_per_packet_;
 }
 
diff --git a/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.h b/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.h
index 9f37fe6..ce87b2c 100644
--- a/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.h
+++ b/webrtc/modules/audio_coding/codecs/g722/audio_encoder_g722.h
@@ -15,6 +15,7 @@
 
 #include "webrtc/api/audio_codecs/audio_encoder.h"
 #include "webrtc/api/audio_codecs/audio_format.h"
+#include "webrtc/api/audio_codecs/g722/audio_encoder_g722_config.h"
 #include "webrtc/base/buffer.h"
 #include "webrtc/base/constructormagic.h"
 #include "webrtc/modules/audio_coding/codecs/g722/g722_interface.h"
@@ -23,20 +24,15 @@
 
 struct CodecInst;
 
-class AudioEncoderG722 final : public AudioEncoder {
+class AudioEncoderG722Impl final : public AudioEncoder {
  public:
-  struct Config {
-    bool IsOk() const;
+  static rtc::Optional<AudioEncoderG722Config> SdpToConfig(
+      const SdpAudioFormat& format);
 
-    int payload_type = 9;
-    int frame_size_ms = 20;
-    size_t num_channels = 1;
-  };
-
-  explicit AudioEncoderG722(const Config& config);
-  explicit AudioEncoderG722(const CodecInst& codec_inst);
-  AudioEncoderG722(int payload_type, const SdpAudioFormat& format);
-  ~AudioEncoderG722() override;
+  AudioEncoderG722Impl(const AudioEncoderG722Config& config, int payload_type);
+  explicit AudioEncoderG722Impl(const CodecInst& codec_inst);
+  AudioEncoderG722Impl(int payload_type, const SdpAudioFormat& format);
+  ~AudioEncoderG722Impl() override;
 
   static constexpr const char* GetPayloadName() { return "G722"; }
   static rtc::Optional<AudioCodecInfo> QueryAudioEncoder(
@@ -74,7 +70,7 @@
   uint32_t first_timestamp_in_buffer_;
   const std::unique_ptr<EncoderState[]> encoders_;
   rtc::Buffer interleave_buffer_;
-  RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderG722);
+  RTC_DISALLOW_COPY_AND_ASSIGN(AudioEncoderG722Impl);
 };
 
 }  // namespace webrtc
diff --git a/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc b/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc
index b1a07ed..8dcb416 100644
--- a/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc
+++ b/webrtc/modules/audio_coding/neteq/audio_decoder_unittest.cc
@@ -404,11 +404,10 @@
     data_length_ = 10 * frame_size_;
     decoder_ = new AudioDecoderG722Impl;
     assert(decoder_);
-    AudioEncoderG722::Config config;
+    AudioEncoderG722Config config;
     config.frame_size_ms = 10;
-    config.payload_type = payload_type_;
     config.num_channels = 1;
-    audio_encoder_.reset(new AudioEncoderG722(config));
+    audio_encoder_.reset(new AudioEncoderG722Impl(config, payload_type_));
   }
 };
 
@@ -421,11 +420,10 @@
     data_length_ = 10 * frame_size_;
     decoder_ = new AudioDecoderG722Stereo;
     assert(decoder_);
-    AudioEncoderG722::Config config;
+    AudioEncoderG722Config config;
     config.frame_size_ms = 10;
-    config.payload_type = payload_type_;
     config.num_channels = 2;
-    audio_encoder_.reset(new AudioEncoderG722(config));
+    audio_encoder_.reset(new AudioEncoderG722Impl(config, payload_type_));
   }
 };