Wire up external encoders.

R=pthatcher@webrtc.org
BUG=1788

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@7440 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/media/webrtc/constants.h b/talk/media/webrtc/constants.h
index 68f664b..5390c0d 100755
--- a/talk/media/webrtc/constants.h
+++ b/talk/media/webrtc/constants.h
@@ -33,6 +33,7 @@
 extern const int kVideoRtpBufferSize;
 
 extern const char kVp8CodecName[];
+extern const char kH264CodecName[];
 
 extern const int kDefaultFramerate;
 extern const int kMinVideoBitrate;
diff --git a/talk/media/webrtc/fakewebrtcvideoengine.h b/talk/media/webrtc/fakewebrtcvideoengine.h
index e6de35b..729179b 100644
--- a/talk/media/webrtc/fakewebrtcvideoengine.h
+++ b/talk/media/webrtc/fakewebrtcvideoengine.h
@@ -38,8 +38,10 @@
 #include "talk/media/webrtc/webrtcvideoencoderfactory.h"
 #include "talk/media/webrtc/webrtcvie.h"
 #include "webrtc/base/basictypes.h"
+#include "webrtc/base/criticalsection.h"
 #include "webrtc/base/gunit.h"
 #include "webrtc/base/stringutils.h"
+#include "webrtc/base/thread_annotations.h"
 
 namespace cricket {
 
@@ -146,7 +148,7 @@
 // Fake class for mocking out webrtc::VideoEnoder
 class FakeWebRtcVideoEncoder : public webrtc::VideoEncoder {
  public:
-  FakeWebRtcVideoEncoder() {}
+  FakeWebRtcVideoEncoder() : num_frames_encoded_(0) {}
 
   virtual int32 InitEncode(const webrtc::VideoCodec* codecSettings,
                            int32 numberOfCores,
@@ -158,6 +160,8 @@
       const webrtc::I420VideoFrame& inputImage,
             const webrtc::CodecSpecificInfo* codecSpecificInfo,
             const std::vector<webrtc::VideoFrameType>* frame_types) {
+    rtc::CritScope lock(&crit_);
+    ++num_frames_encoded_;
     return WEBRTC_VIDEO_CODEC_OK;
   }
 
@@ -179,6 +183,15 @@
                          uint32 frameRate) {
     return WEBRTC_VIDEO_CODEC_OK;
   }
+
+  int GetNumEncodedFrames() {
+    rtc::CritScope lock(&crit_);
+    return num_frames_encoded_;
+  }
+
+ private:
+  rtc::CriticalSection crit_;
+  int num_frames_encoded_ GUARDED_BY(crit_);
 };
 
 // Fake class for mocking out WebRtcVideoEncoderFactory.
diff --git a/talk/media/webrtc/webrtcvideoengine2.cc b/talk/media/webrtc/webrtcvideoengine2.cc
index d8b5616..628ed50 100644
--- a/talk/media/webrtc/webrtcvideoengine2.cc
+++ b/talk/media/webrtc/webrtcvideoengine2.cc
@@ -49,6 +49,65 @@
   ASSERT(false)
 
 namespace cricket {
+namespace {
+
+static bool CodecNameMatches(const std::string& name1,
+                             const std::string& name2) {
+  return _stricmp(name1.c_str(), name2.c_str()) == 0;
+}
+
+// True if codec is supported by a software implementation that's always
+// available.
+static bool CodecIsInternallySupported(const std::string& codec_name) {
+  return CodecNameMatches(codec_name, kVp8CodecName);
+}
+
+static std::string CodecVectorToString(const std::vector<VideoCodec>& codecs) {
+  std::stringstream out;
+  out << '{';
+  for (size_t i = 0; i < codecs.size(); ++i) {
+    out << codecs[i].ToString();
+    if (i != codecs.size() - 1) {
+      out << ", ";
+    }
+  }
+  out << '}';
+  return out.str();
+}
+
+static bool ValidateCodecFormats(const std::vector<VideoCodec>& codecs) {
+  bool has_video = false;
+  for (size_t i = 0; i < codecs.size(); ++i) {
+    if (!codecs[i].ValidateCodecFormat()) {
+      return false;
+    }
+    if (codecs[i].GetCodecType() == VideoCodec::CODEC_VIDEO) {
+      has_video = true;
+    }
+  }
+  if (!has_video) {
+    LOG(LS_ERROR) << "Setting codecs without a video codec is invalid: "
+                  << CodecVectorToString(codecs);
+    return false;
+  }
+  return true;
+}
+
+static std::string RtpExtensionsToString(
+    const std::vector<RtpHeaderExtension>& extensions) {
+  std::stringstream out;
+  out << '{';
+  for (size_t i = 0; i < extensions.size(); ++i) {
+    out << "{" << extensions[i].uri << ": " << extensions[i].id << "}";
+    if (i != extensions.size() - 1) {
+      out << ", ";
+    }
+  }
+  out << '}';
+  return out.str();
+}
+
+}  // namespace
 
 // This constant is really an on/off, lower-level configurable NACK history
 // duration hasn't been implemented.
@@ -58,6 +117,13 @@
 
 static const int kDefaultRtcpReceiverReportSsrc = 1;
 
+// External video encoders are given payloads 120-127. This also means that we
+// only support up to 8 external payload types.
+static const int kExternalVideoPayloadTypeBase = 120;
+#ifndef NDEBUG
+static const size_t kMaxExternalVideoCodecs = 8;
+#endif
+
 struct VideoCodecPref {
   int payload_type;
   int width;
@@ -66,6 +132,8 @@
   int rtx_payload_type;
 } kDefaultVideoCodecPref = {100, 640, 400, kVp8CodecName, 96};
 
+const char kH264CodecName[] = "H264";
+
 VideoCodecPref kRedPref = {116, -1, -1, kRedCodecName, -1};
 VideoCodecPref kUlpfecPref = {117, -1, -1, kUlpfecCodecName, -1};
 
@@ -169,7 +237,6 @@
     const VideoCodec& codec,
     const VideoOptions& options,
     size_t num_streams) {
-  assert(SupportsCodec(codec));
   if (num_streams != 1) {
     LOG(LS_ERROR) << "Unsupported number of streams: " << num_streams;
     return std::vector<webrtc::VideoStream>();
@@ -196,24 +263,10 @@
   return streams;
 }
 
-webrtc::VideoEncoder* WebRtcVideoEncoderFactory2::CreateVideoEncoder(
-    const VideoCodec& codec,
-    const VideoOptions& options) {
-  assert(SupportsCodec(codec));
-  if (_stricmp(codec.name.c_str(), kVp8CodecName) == 0) {
-    return webrtc::VideoEncoder::Create(webrtc::VideoEncoder::kVp8);
-  }
-  // This shouldn't happen, we should be able to create encoders for all codecs
-  // we support.
-  assert(false);
-  return NULL;
-}
-
 void* WebRtcVideoEncoderFactory2::CreateVideoEncoderSettings(
     const VideoCodec& codec,
     const VideoOptions& options) {
-  assert(SupportsCodec(codec));
-  if (_stricmp(codec.name.c_str(), kVp8CodecName) == 0) {
+  if (CodecNameMatches(codec.name, kVp8CodecName)) {
     webrtc::VideoCodecVP8* settings = new webrtc::VideoCodecVP8(
         webrtc::VideoEncoder::GetDefaultVp8Settings());
     options.video_noise_reduction.Get(&settings->denoisingOn);
@@ -225,19 +278,14 @@
 void WebRtcVideoEncoderFactory2::DestroyVideoEncoderSettings(
     const VideoCodec& codec,
     void* encoder_settings) {
-  assert(SupportsCodec(codec));
   if (encoder_settings == NULL) {
     return;
   }
-  if (_stricmp(codec.name.c_str(), kVp8CodecName) == 0) {
+  if (CodecNameMatches(codec.name, kVp8CodecName)) {
     delete reinterpret_cast<webrtc::VideoCodecVP8*>(encoder_settings);
   }
 }
 
-bool WebRtcVideoEncoderFactory2::SupportsCodec(const VideoCodec& codec) {
-  return _stricmp(codec.name.c_str(), kVp8CodecName) == 0;
-}
-
 DefaultUnsignalledSsrcHandler::DefaultUnsignalledSsrcHandler()
     : default_recv_ssrc_(0), default_renderer_(NULL) {}
 
@@ -284,7 +332,6 @@
 WebRtcVideoEngine2::WebRtcVideoEngine2()
     : worker_thread_(NULL),
       voice_engine_(NULL),
-      video_codecs_(DefaultVideoCodecs()),
       default_codec_format_(kDefaultVideoCodecPref.width,
                             kDefaultVideoCodecPref.height,
                             FPS_TO_INTERVAL(kDefaultFramerate),
@@ -295,6 +342,7 @@
       external_decoder_factory_(NULL),
       external_encoder_factory_(NULL) {
   LOG(LS_INFO) << "WebRtcVideoEngine2::WebRtcVideoEngine2()";
+  video_codecs_ = GetSupportedCodecs();
   rtp_header_extensions_.push_back(
       RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension,
                          kRtpTimestampOffsetHeaderExtensionDefaultId));
@@ -312,6 +360,7 @@
 }
 
 void WebRtcVideoEngine2::SetCallFactory(WebRtcCallFactory* call_factory) {
+  assert(!initialized_);
   call_factory_ = call_factory;
 }
 
@@ -344,7 +393,7 @@
     const VideoEncoderConfig& config) {
   const VideoCodec& codec = config.max_codec;
   // TODO(pbos): Make use of external encoder factory.
-  if (!GetVideoEncoderFactory()->SupportsCodec(codec)) {
+  if (!CodecIsInternallySupported(codec.name)) {
     LOG(LS_ERROR) << "SetDefaultEncoderConfig, codec not supported:"
                   << codec.ToString();
     return false;
@@ -366,11 +415,16 @@
 
 WebRtcVideoChannel2* WebRtcVideoEngine2::CreateChannel(
     VoiceMediaChannel* voice_channel) {
+  assert(initialized_);
   LOG(LS_INFO) << "CreateChannel: "
                << (voice_channel != NULL ? "With" : "Without")
                << " voice channel.";
-  WebRtcVideoChannel2* channel = new WebRtcVideoChannel2(
-      call_factory_, voice_channel, GetVideoEncoderFactory());
+  WebRtcVideoChannel2* channel =
+      new WebRtcVideoChannel2(call_factory_,
+                              voice_channel,
+                              external_encoder_factory_,
+                              external_decoder_factory_,
+                              GetVideoEncoderFactory());
   if (!channel->Init()) {
     delete channel;
     return NULL;
@@ -400,12 +454,16 @@
 
 void WebRtcVideoEngine2::SetExternalDecoderFactory(
     WebRtcVideoDecoderFactory* decoder_factory) {
+  assert(!initialized_);
   external_decoder_factory_ = decoder_factory;
 }
 
 void WebRtcVideoEngine2::SetExternalEncoderFactory(
     WebRtcVideoEncoderFactory* encoder_factory) {
+  assert(!initialized_);
   external_encoder_factory_ = encoder_factory;
+
+  video_codecs_ = GetSupportedCodecs();
 }
 
 bool WebRtcVideoEngine2::EnableTimedRender() {
@@ -495,6 +553,35 @@
   return &default_video_encoder_factory_;
 }
 
+std::vector<VideoCodec> WebRtcVideoEngine2::GetSupportedCodecs() const {
+  std::vector<VideoCodec> supported_codecs = DefaultVideoCodecs();
+
+  if (external_encoder_factory_ == NULL) {
+    return supported_codecs;
+  }
+
+  assert(external_encoder_factory_->codecs().size() <= kMaxExternalVideoCodecs);
+  const std::vector<WebRtcVideoEncoderFactory::VideoCodec>& codecs =
+      external_encoder_factory_->codecs();
+  for (size_t i = 0; i < codecs.size(); ++i) {
+    // Don't add internally-supported codecs twice.
+    if (CodecIsInternallySupported(codecs[i].name)) {
+      continue;
+    }
+
+    VideoCodec codec(kExternalVideoPayloadTypeBase + static_cast<int>(i),
+                     codecs[i].name,
+                     codecs[i].max_width,
+                     codecs[i].max_height,
+                     codecs[i].max_fps,
+                     0);
+
+    AddDefaultFeedbackParams(&codec);
+    supported_codecs.push_back(codec);
+  }
+  return supported_codecs;
+}
+
 // Thin map between VideoFrame and an existing webrtc::I420VideoFrame
 // to avoid having to copy the rendered VideoFrame prematurely.
 // This implementation is only safe to use in a const context and should never
@@ -656,8 +743,12 @@
 WebRtcVideoChannel2::WebRtcVideoChannel2(
     WebRtcCallFactory* call_factory,
     VoiceMediaChannel* voice_channel,
+    WebRtcVideoEncoderFactory* external_encoder_factory,
+    WebRtcVideoDecoderFactory* external_decoder_factory,
     WebRtcVideoEncoderFactory2* encoder_factory)
     : unsignalled_ssrc_handler_(&default_unsignalled_ssrc_handler_),
+      external_encoder_factory_(external_encoder_factory),
+      external_decoder_factory_(external_decoder_factory),
       encoder_factory_(encoder_factory) {
   // TODO(pbos): Connect the video and audio with |voice_channel|.
   webrtc::Call::Config config(this);
@@ -696,55 +787,6 @@
 
 bool WebRtcVideoChannel2::Init() { return true; }
 
-namespace {
-
-static std::string CodecVectorToString(const std::vector<VideoCodec>& codecs) {
-  std::stringstream out;
-  out << '{';
-  for (size_t i = 0; i < codecs.size(); ++i) {
-    out << codecs[i].ToString();
-    if (i != codecs.size() - 1) {
-      out << ", ";
-    }
-  }
-  out << '}';
-  return out.str();
-}
-
-static bool ValidateCodecFormats(const std::vector<VideoCodec>& codecs) {
-  bool has_video = false;
-  for (size_t i = 0; i < codecs.size(); ++i) {
-    if (!codecs[i].ValidateCodecFormat()) {
-      return false;
-    }
-    if (codecs[i].GetCodecType() == VideoCodec::CODEC_VIDEO) {
-      has_video = true;
-    }
-  }
-  if (!has_video) {
-    LOG(LS_ERROR) << "Setting codecs without a video codec is invalid: "
-                  << CodecVectorToString(codecs);
-    return false;
-  }
-  return true;
-}
-
-static std::string RtpExtensionsToString(
-    const std::vector<RtpHeaderExtension>& extensions) {
-  std::stringstream out;
-  out << '{';
-  for (size_t i = 0; i < extensions.size(); ++i) {
-    out << "{" << extensions[i].uri << ": " << extensions[i].id << "}";
-    if (i != extensions.size() - 1) {
-      out << ", ";
-    }
-  }
-  out << '}';
-  return out.str();
-}
-
-}  // namespace
-
 bool WebRtcVideoChannel2::SetRecvCodecs(const std::vector<VideoCodec>& codecs) {
   LOG(LS_INFO) << "SetRecvCodecs: " << CodecVectorToString(codecs);
   if (!ValidateCodecFormats(codecs)) {
@@ -760,7 +802,7 @@
   // TODO(pbos): Add a decoder factory which controls supported codecs.
   // Blocked on webrtc:2854.
   for (size_t i = 0; i < mapped_codecs.size(); ++i) {
-    if (_stricmp(mapped_codecs[i].codec.name.c_str(), kVp8CodecName) != 0) {
+    if (!CodecNameMatches(mapped_codecs[i].codec.name, kVp8CodecName)) {
       LOG(LS_ERROR) << "SetRecvCodecs called with unsupported codec: '"
                     << mapped_codecs[i].codec.name << "'";
       return false;
@@ -881,6 +923,7 @@
 
   WebRtcVideoSendStream* stream =
       new WebRtcVideoSendStream(call_.get(),
+                                external_encoder_factory_,
                                 encoder_factory_,
                                 options_,
                                 send_codec_,
@@ -1307,15 +1350,18 @@
 
 WebRtcVideoChannel2::WebRtcVideoSendStream::WebRtcVideoSendStream(
     webrtc::Call* call,
+    WebRtcVideoEncoderFactory* external_encoder_factory,
     WebRtcVideoEncoderFactory2* encoder_factory,
     const VideoOptions& options,
     const Settable<VideoCodecSettings>& codec_settings,
     const StreamParams& sp,
     const std::vector<webrtc::RtpExtension>& rtp_extensions)
     : call_(call),
+      external_encoder_factory_(external_encoder_factory),
       encoder_factory_(encoder_factory),
       stream_(NULL),
       parameters_(webrtc::VideoSendStream::Config(), options, codec_settings),
+      allocated_encoder_(NULL, webrtc::kVideoCodecUnknown, false),
       capturer_(NULL),
       sending_(false),
       muted_(false) {
@@ -1338,7 +1384,7 @@
   if (stream_ != NULL) {
     call_->DestroyVideoSendStream(stream_);
   }
-  delete parameters_.config.encoder_settings.encoder;
+  DestroyVideoEncoder(&allocated_encoder_);
 }
 
 static void SetWebRtcFrameToBlack(webrtc::I420VideoFrame* video_frame) {
@@ -1505,11 +1551,60 @@
     parameters_.options = options;
   }
 }
+
 void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodec(
     const VideoCodecSettings& codec_settings) {
   rtc::CritScope cs(&lock_);
   SetCodecAndOptions(codec_settings, parameters_.options);
 }
+
+webrtc::VideoCodecType CodecTypeFromName(const std::string& name) {
+  if (CodecNameMatches(name, kVp8CodecName)) {
+    return webrtc::kVideoCodecVP8;
+  } else if (CodecNameMatches(name, kH264CodecName)) {
+    return webrtc::kVideoCodecH264;
+  }
+  return webrtc::kVideoCodecUnknown;
+}
+
+WebRtcVideoChannel2::WebRtcVideoSendStream::AllocatedEncoder
+WebRtcVideoChannel2::WebRtcVideoSendStream::CreateVideoEncoder(
+    const VideoCodec& codec) {
+  webrtc::VideoCodecType type = CodecTypeFromName(codec.name);
+
+  // Do not re-create encoders of the same type.
+  if (type == allocated_encoder_.type && allocated_encoder_.encoder != NULL) {
+    return allocated_encoder_;
+  }
+
+  if (external_encoder_factory_ != NULL) {
+    webrtc::VideoEncoder* encoder =
+        external_encoder_factory_->CreateVideoEncoder(type);
+    if (encoder != NULL) {
+      return AllocatedEncoder(encoder, type, true);
+    }
+  }
+
+  if (type == webrtc::kVideoCodecVP8) {
+    return AllocatedEncoder(
+        webrtc::VideoEncoder::Create(webrtc::VideoEncoder::kVp8), type, false);
+  }
+
+  // This shouldn't happen, we should not be trying to create something we don't
+  // support.
+  assert(false);
+  return AllocatedEncoder(NULL, webrtc::kVideoCodecUnknown, false);
+}
+
+void WebRtcVideoChannel2::WebRtcVideoSendStream::DestroyVideoEncoder(
+    AllocatedEncoder* encoder) {
+  if (encoder->external) {
+    external_encoder_factory_->DestroyVideoEncoder(encoder->encoder);
+  } else {
+    delete encoder->encoder;
+  }
+}
+
 void WebRtcVideoChannel2::WebRtcVideoSendStream::SetCodecAndOptions(
     const VideoCodecSettings& codec_settings,
     const VideoOptions& options) {
@@ -1525,10 +1620,8 @@
                         VideoFormat::FpsToInterval(30),
                         FOURCC_I420);
 
-  webrtc::VideoEncoder* old_encoder =
-      parameters_.config.encoder_settings.encoder;
-  parameters_.config.encoder_settings.encoder =
-      encoder_factory_->CreateVideoEncoder(codec_settings.codec, options);
+  AllocatedEncoder new_encoder = CreateVideoEncoder(codec_settings.codec);
+  parameters_.config.encoder_settings.encoder = new_encoder.encoder;
   parameters_.config.encoder_settings.payload_name = codec_settings.codec.name;
   parameters_.config.encoder_settings.payload_type = codec_settings.codec.id;
   parameters_.config.rtp.fec = codec_settings.fec;
@@ -1552,7 +1645,10 @@
   parameters_.options = options;
 
   RecreateWebRtcStream();
-  delete old_encoder;
+  if (allocated_encoder_.encoder != new_encoder.encoder) {
+    DestroyVideoEncoder(&allocated_encoder_);
+    allocated_encoder_ = new_encoder;
+  }
 }
 
 void WebRtcVideoChannel2::WebRtcVideoSendStream::SetRtpExtensions(
@@ -1957,8 +2053,21 @@
     const std::vector<WebRtcVideoChannel2::VideoCodecSettings>& mapped_codecs) {
   std::vector<VideoCodecSettings> supported_codecs;
   for (size_t i = 0; i < mapped_codecs.size(); ++i) {
-    if (encoder_factory_->SupportsCodec(mapped_codecs[i].codec)) {
-      supported_codecs.push_back(mapped_codecs[i]);
+    const VideoCodecSettings& codec = mapped_codecs[i];
+    if (CodecIsInternallySupported(codec.codec.name)) {
+      supported_codecs.push_back(codec);
+    }
+
+    if (external_encoder_factory_ == NULL) {
+      continue;
+    }
+    const std::vector<WebRtcVideoEncoderFactory::VideoCodec> external_codecs =
+        external_encoder_factory_->codecs();
+    for (size_t c = 0; c < external_codecs.size(); ++c) {
+      if (CodecNameMatches(codec.codec.name, external_codecs[c].name)) {
+        supported_codecs.push_back(codec);
+        break;
+      }
     }
   }
   return supported_codecs;
diff --git a/talk/media/webrtc/webrtcvideoengine2.h b/talk/media/webrtc/webrtcvideoengine2.h
index de591f0..6b9eaa7 100644
--- a/talk/media/webrtc/webrtcvideoengine2.h
+++ b/talk/media/webrtc/webrtcvideoengine2.h
@@ -72,7 +72,6 @@
 class WebRtcEncoderObserver;
 class WebRtcLocalStreamInfo;
 class WebRtcRenderAdapter;
-class WebRtcVideoChannel2;
 class WebRtcVideoChannelRecvInfo;
 class WebRtcVideoChannelSendInfo;
 class WebRtcVoiceEngine;
@@ -80,7 +79,6 @@
 struct CapturedFrame;
 struct Device;
 
-class WebRtcVideoChannel2;
 class WebRtcVideoRenderer;
 
 class UnsignalledSsrcHandler {
@@ -116,17 +114,11 @@
       const VideoOptions& options,
       size_t num_streams);
 
-  virtual webrtc::VideoEncoder* CreateVideoEncoder(
-      const VideoCodec& codec,
-      const VideoOptions& options);
-
   virtual void* CreateVideoEncoderSettings(const VideoCodec& codec,
                                            const VideoOptions& options);
 
   virtual void DestroyVideoEncoderSettings(const VideoCodec& codec,
                                            void* encoder_settings);
-
-  virtual bool SupportsCodec(const cricket::VideoCodec& codec);
 };
 
 // CallFactory, overridden for testing to verify that webrtc::Call is configured
@@ -192,6 +184,8 @@
   virtual WebRtcVideoEncoderFactory2* GetVideoEncoderFactory();
 
  private:
+  std::vector<VideoCodec> GetSupportedCodecs() const;
+
   rtc::Thread* worker_thread_;
   WebRtcVoiceEngine* voice_engine_;
   std::vector<VideoCodec> video_codecs_;
@@ -217,6 +211,8 @@
  public:
   WebRtcVideoChannel2(WebRtcCallFactory* call_factory,
                       VoiceMediaChannel* voice_channel,
+                      WebRtcVideoEncoderFactory* external_encoder_factory,
+                      WebRtcVideoDecoderFactory* external_decoder_factory,
                       WebRtcVideoEncoderFactory2* encoder_factory);
   ~WebRtcVideoChannel2();
   bool Init();
@@ -292,6 +288,7 @@
    public:
     WebRtcVideoSendStream(
         webrtc::Call* call,
+        WebRtcVideoEncoderFactory* external_encoder_factory,
         WebRtcVideoEncoderFactory2* encoder_factory,
         const VideoOptions& options,
         const Settable<VideoCodecSettings>& codec_settings,
@@ -337,6 +334,19 @@
       webrtc::VideoEncoderConfig encoder_config;
     };
 
+    struct AllocatedEncoder {
+      AllocatedEncoder(webrtc::VideoEncoder* encoder,
+                       webrtc::VideoCodecType type,
+                       bool external)
+          : encoder(encoder), type(type), external(external) {}
+      webrtc::VideoEncoder* encoder;
+      webrtc::VideoCodecType type;
+      bool external;
+    };
+
+    AllocatedEncoder CreateVideoEncoder(const VideoCodec& codec)
+        EXCLUSIVE_LOCKS_REQUIRED(lock_);
+    void DestroyVideoEncoder(AllocatedEncoder* encoder);
     void SetCodecAndOptions(const VideoCodecSettings& codec,
                             const VideoOptions& options)
         EXCLUSIVE_LOCKS_REQUIRED(lock_);
@@ -346,11 +356,13 @@
         EXCLUSIVE_LOCKS_REQUIRED(lock_);
 
     webrtc::Call* const call_;
+    WebRtcVideoEncoderFactory* const external_encoder_factory_;
     WebRtcVideoEncoderFactory2* const encoder_factory_;
 
     rtc::CriticalSection lock_;
     webrtc::VideoSendStream* stream_ GUARDED_BY(lock_);
     VideoSendStreamParameters parameters_ GUARDED_BY(lock_);
+    AllocatedEncoder allocated_encoder_ GUARDED_BY(lock_);
 
     VideoCapturer* capturer_ GUARDED_BY(lock_);
     bool sending_ GUARDED_BY(lock_);
@@ -436,6 +448,8 @@
   Settable<VideoCodecSettings> send_codec_;
   std::vector<webrtc::RtpExtension> send_rtp_extensions_;
 
+  WebRtcVideoEncoderFactory* const external_encoder_factory_;
+  WebRtcVideoDecoderFactory* const external_decoder_factory_;
   WebRtcVideoEncoderFactory2* const encoder_factory_;
   std::vector<VideoCodecSettings> recv_codecs_;
   std::vector<webrtc::RtpExtension> recv_rtp_extensions_;
diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
index b718992..0cac1d5 100644
--- a/talk/media/webrtc/webrtcvideoengine2_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
@@ -30,6 +30,7 @@
 
 #include "talk/media/base/testutils.h"
 #include "talk/media/base/videoengine_unittest.h"
+#include "talk/media/webrtc/fakewebrtcvideoengine.h"
 #include "talk/media/webrtc/webrtcvideochannelfactory.h"
 #include "talk/media/webrtc/webrtcvideoengine2.h"
 #include "talk/media/webrtc/webrtcvideoengine2_unittest.h"
@@ -40,11 +41,11 @@
 namespace {
 static const cricket::VideoCodec kVp8Codec720p(100, "VP8", 1280, 720, 30, 0);
 static const cricket::VideoCodec kVp8Codec360p(100, "VP8", 640, 360, 30, 0);
-static const cricket::VideoCodec kVp8Codec270p(100, "VP8", 480, 270, 30, 0);
-static const cricket::VideoCodec kVp8Codec180p(100, "VP8", 320, 180, 30, 0);
 
 static const cricket::VideoCodec kVp8Codec(100, "VP8", 640, 400, 30, 0);
 static const cricket::VideoCodec kVp9Codec(101, "VP9", 640, 400, 30, 0);
+static const cricket::VideoCodec kH264Codec(102, "H264", 640, 400, 30, 0);
+
 static const cricket::VideoCodec kRedCodec(116, "red", 0, 0, 0, 0);
 static const cricket::VideoCodec kUlpfecCodec(117, "ulpfec", 0, 0, 0, 0);
 
@@ -303,7 +304,7 @@
   network_state_ = state;
 }
 
-class WebRtcVideoEngine2Test : public testing::Test {
+class WebRtcVideoEngine2Test : public ::testing::Test {
  public:
   WebRtcVideoEngine2Test() {
     std::vector<VideoCodec> engine_codecs = engine_.codecs();
@@ -326,6 +327,9 @@
   }
 
  protected:
+  VideoMediaChannel* SetUpForExternalEncoderFactory(
+      cricket::WebRtcVideoEncoderFactory* encoder_factory,
+      const std::vector<VideoCodec>& codecs);
   WebRtcVideoEngine2 engine_;
   VideoCodec default_codec_;
   VideoCodec default_red_codec_;
@@ -422,6 +426,7 @@
 }
 
 TEST_F(WebRtcVideoEngine2Test, SetSendFailsBeforeSettingCodecs) {
+  engine_.Init(rtc::Thread::Current());
   rtc::scoped_ptr<VideoMediaChannel> channel(engine_.CreateChannel(NULL));
 
   EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(123)));
@@ -433,12 +438,110 @@
 }
 
 TEST_F(WebRtcVideoEngine2Test, GetStatsWithoutSendCodecsSetDoesNotCrash) {
+  engine_.Init(rtc::Thread::Current());
   rtc::scoped_ptr<VideoMediaChannel> channel(engine_.CreateChannel(NULL));
   EXPECT_TRUE(channel->AddSendStream(StreamParams::CreateLegacy(123)));
   VideoMediaInfo info;
   channel->GetStats(&info);
 }
 
+TEST_F(WebRtcVideoEngine2Test, UseExternalFactoryForVp8WhenSupported) {
+  cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
+  encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecVP8, "VP8");
+  std::vector<cricket::VideoCodec> codecs;
+  codecs.push_back(kVp8Codec);
+
+  rtc::scoped_ptr<VideoMediaChannel> channel(
+      SetUpForExternalEncoderFactory(&encoder_factory, codecs));
+
+  EXPECT_TRUE(
+      channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  ASSERT_EQ(1u, encoder_factory.encoders().size());
+  EXPECT_TRUE(channel->SetSend(true));
+
+  cricket::FakeVideoCapturer capturer;
+  EXPECT_TRUE(channel->SetCapturer(kSsrc, &capturer));
+  EXPECT_EQ(cricket::CS_RUNNING,
+            capturer.Start(capturer.GetSupportedFormats()->front()));
+  EXPECT_TRUE(capturer.CaptureFrame());
+
+  EXPECT_TRUE_WAIT(encoder_factory.encoders()[0]->GetNumEncodedFrames() > 0,
+                   kTimeout);
+
+  // Setting codecs of the same type should not reallocate the encoder.
+  EXPECT_TRUE(channel->SetSendCodecs(codecs));
+  EXPECT_EQ(1, encoder_factory.GetNumCreatedEncoders());
+
+  // Remove stream previously added to free the external encoder instance.
+  EXPECT_TRUE(channel->RemoveSendStream(kSsrc));
+  EXPECT_EQ(0u, encoder_factory.encoders().size());
+}
+
+VideoMediaChannel* WebRtcVideoEngine2Test::SetUpForExternalEncoderFactory(
+    cricket::WebRtcVideoEncoderFactory* encoder_factory,
+    const std::vector<VideoCodec>& codecs) {
+  engine_.SetExternalEncoderFactory(encoder_factory);
+  engine_.Init(rtc::Thread::Current());
+
+  VideoMediaChannel* channel = engine_.CreateChannel(NULL);
+  EXPECT_TRUE(channel->SetSendCodecs(codecs));
+
+  return channel;
+}
+
+TEST_F(WebRtcVideoEngine2Test, ChannelWithExternalH264CanChangeToInternalVp8) {
+  cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
+  encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264");
+  std::vector<cricket::VideoCodec> codecs;
+  codecs.push_back(kH264Codec);
+
+  rtc::scoped_ptr<VideoMediaChannel> channel(
+      SetUpForExternalEncoderFactory(&encoder_factory, codecs));
+
+  EXPECT_TRUE(
+      channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  ASSERT_EQ(1u, encoder_factory.encoders().size());
+
+  codecs.clear();
+  codecs.push_back(kVp8Codec);
+  EXPECT_TRUE(channel->SetSendCodecs(codecs));
+
+  ASSERT_EQ(0u, encoder_factory.encoders().size());
+}
+
+TEST_F(WebRtcVideoEngine2Test,
+       DontUseExternalEncoderFactoryForUnsupportedCodecs) {
+  cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
+  encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264");
+  std::vector<cricket::VideoCodec> codecs;
+  codecs.push_back(kVp8Codec);
+
+  rtc::scoped_ptr<VideoMediaChannel> channel(
+      SetUpForExternalEncoderFactory(&encoder_factory, codecs));
+
+  EXPECT_TRUE(
+      channel->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+  ASSERT_EQ(0u, encoder_factory.encoders().size());
+}
+
+// Test external codec with be added to the end of the supported codec list.
+TEST_F(WebRtcVideoEngine2Test, ReportSupportedExternalCodecs) {
+  cricket::FakeWebRtcVideoEncoderFactory encoder_factory;
+  encoder_factory.AddSupportedVideoCodecType(webrtc::kVideoCodecH264, "H264");
+  engine_.SetExternalEncoderFactory(&encoder_factory);
+
+  engine_.Init(rtc::Thread::Current());
+
+  std::vector<cricket::VideoCodec> codecs(engine_.codecs());
+  ASSERT_GE(codecs.size(), 2u);
+  cricket::VideoCodec internal_codec = codecs.front();
+  cricket::VideoCodec external_codec = codecs.back();
+
+  // The external codec will appear at last.
+  EXPECT_EQ("VP8", internal_codec.name);
+  EXPECT_EQ("H264", external_codec.name);
+}
+
 class WebRtcVideoEngine2BaseTest
     : public VideoEngineTest<cricket::WebRtcVideoEngine2> {
  protected:
@@ -581,6 +684,7 @@
   WebRtcVideoChannel2Test() : fake_call_(NULL) {}
   virtual void SetUp() OVERRIDE {
     engine_.SetCallFactory(this);
+    engine_.Init(rtc::Thread::Current());
     channel_.reset(engine_.CreateChannel(NULL));
     ASSERT_TRUE(fake_call_ != NULL) << "Call not created through factory.";
     last_ssrc_ = 123;
@@ -1604,28 +1708,6 @@
   FAIL() << "Not implemented.";  // TODO(pbos): Implement.
 }
 
-TEST_F(WebRtcVideoChannel2Test,
-       DISABLED_DontRegisterEncoderIfFactoryIsNotGiven) {
-  FAIL() << "Not implemented.";  // TODO(pbos): Implement.
-}
-
-TEST_F(WebRtcVideoChannel2Test, DISABLED_RegisterEncoderIfFactoryIsGiven) {
-  FAIL() << "Not implemented.";  // TODO(pbos): Implement.
-}
-
-TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterEncoderMultipleTimes) {
-  FAIL() << "Not implemented.";  // TODO(pbos): Implement.
-}
-
-TEST_F(WebRtcVideoChannel2Test,
-       DISABLED_RegisterEncoderWithMultipleSendStreams) {
-  FAIL() << "Not implemented.";  // TODO(pbos): Implement.
-}
-
-TEST_F(WebRtcVideoChannel2Test, DISABLED_DontRegisterEncoderForNonVP8) {
-  FAIL() << "Not implemented.";  // TODO(pbos): Implement.
-}
-
 TEST_F(WebRtcVideoChannel2Test, DISABLED_FeedbackParamsForNonVP8) {
   FAIL() << "Not implemented.";  // TODO(pbos): Implement.
 }