Refactor VP8 encoder creation logic

Now decision between using SimulcastEncoderAdapter and using VP8 encoder
is postponed before codec is initialized for VP8 internal codecs. This is done
be new VP8EncoderProxy class. New error code for codec initialization is used
to signal that simulcast parameters are not supported.

Bug: webrtc:7925
Change-Id: I3a82c21bf5dfaaa7fa25350986830523f02c39d8
Reviewed-on: https://webrtc-review.googlesource.com/13980
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20419}
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 2678a31..e98001a 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -162,6 +162,8 @@
     "engine/videodecodersoftwarefallbackwrapper.h",
     "engine/videoencodersoftwarefallbackwrapper.cc",
     "engine/videoencodersoftwarefallbackwrapper.h",
+    "engine/vp8_encoder_simulcast_proxy.cc",
+    "engine/vp8_encoder_simulcast_proxy.h",
     "engine/webrtcmediaengine.cc",
     "engine/webrtcmediaengine.h",
     "engine/webrtcvideocapturer.cc",
@@ -460,6 +462,7 @@
       "engine/simulcast_unittest.cc",
       "engine/videodecodersoftwarefallbackwrapper_unittest.cc",
       "engine/videoencodersoftwarefallbackwrapper_unittest.cc",
+      "engine/vp8_encoder_simulcast_proxy_unittest.cc",
       "engine/webrtcmediaengine_unittest.cc",
       "engine/webrtcvideocapturer_unittest.cc",
       "engine/webrtcvideoencoderfactory_unittest.cc",
@@ -541,6 +544,7 @@
       "../system_wrappers:metrics_default",
       "../test:audio_codec_mocks",
       "../test:test_support",
+      "../test:video_test_common",
       "../voice_engine:voice_engine",
     ]
   }
diff --git a/media/engine/vp8_encoder_simulcast_proxy.cc b/media/engine/vp8_encoder_simulcast_proxy.cc
new file mode 100644
index 0000000..85e37bf
--- /dev/null
+++ b/media/engine/vp8_encoder_simulcast_proxy.cc
@@ -0,0 +1,85 @@
+/*
+ *  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 "media/engine/vp8_encoder_simulcast_proxy.h"
+
+#include "media/engine/scopedvideoencoder.h"
+#include "media/engine/simulcast_encoder_adapter.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+VP8EncoderSimulcastProxy::VP8EncoderSimulcastProxy(
+    cricket::WebRtcVideoEncoderFactory* factory)
+    : factory_(factory), callback_(nullptr) {
+  encoder_ = CreateScopedVideoEncoder(factory_, cricket::VideoCodec("VP8"));
+}
+
+VP8EncoderSimulcastProxy::~VP8EncoderSimulcastProxy() {}
+
+int VP8EncoderSimulcastProxy::Release() {
+  return encoder_->Release();
+}
+
+int VP8EncoderSimulcastProxy::InitEncode(const VideoCodec* inst,
+                                         int number_of_cores,
+                                         size_t max_payload_size) {
+  int ret = encoder_->InitEncode(inst, number_of_cores, max_payload_size);
+  if (ret == WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED) {
+    encoder_.reset(new SimulcastEncoderAdapter(factory_));
+    if (callback_) {
+      encoder_->RegisterEncodeCompleteCallback(callback_);
+    }
+    ret = encoder_->InitEncode(inst, number_of_cores, max_payload_size);
+  }
+  return ret;
+}
+
+int VP8EncoderSimulcastProxy::Encode(
+    const VideoFrame& input_image,
+    const CodecSpecificInfo* codec_specific_info,
+    const std::vector<FrameType>* frame_types) {
+  return encoder_->Encode(input_image, codec_specific_info, frame_types);
+}
+
+int VP8EncoderSimulcastProxy::RegisterEncodeCompleteCallback(
+    EncodedImageCallback* callback) {
+  callback_ = callback;
+  return encoder_->RegisterEncodeCompleteCallback(callback);
+}
+
+int VP8EncoderSimulcastProxy::SetChannelParameters(uint32_t packet_loss,
+                                                   int64_t rtt) {
+  return encoder_->SetChannelParameters(packet_loss, rtt);
+}
+
+int VP8EncoderSimulcastProxy::SetRateAllocation(
+    const BitrateAllocation& bitrate,
+    uint32_t new_framerate) {
+  return encoder_->SetRateAllocation(bitrate, new_framerate);
+}
+
+VideoEncoder::ScalingSettings VP8EncoderSimulcastProxy::GetScalingSettings()
+    const {
+  return encoder_->GetScalingSettings();
+}
+
+int32_t VP8EncoderSimulcastProxy::SetPeriodicKeyFrames(bool enable) {
+  return encoder_->SetPeriodicKeyFrames(enable);
+}
+
+bool VP8EncoderSimulcastProxy::SupportsNativeHandle() const {
+  return encoder_->SupportsNativeHandle();
+}
+
+const char* VP8EncoderSimulcastProxy::ImplementationName() const {
+  return encoder_->ImplementationName();
+}
+
+}  // namespace webrtc
diff --git a/media/engine/vp8_encoder_simulcast_proxy.h b/media/engine/vp8_encoder_simulcast_proxy.h
new file mode 100644
index 0000000..46ccae4
--- /dev/null
+++ b/media/engine/vp8_encoder_simulcast_proxy.h
@@ -0,0 +1,58 @@
+/*
+ *  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 MEDIA_ENGINE_VP8_ENCODER_SIMULCAST_PROXY_H_
+#define MEDIA_ENGINE_VP8_ENCODER_SIMULCAST_PROXY_H_
+
+#include <memory>
+#include <vector>
+
+#include "media/engine/webrtcvideoencoderfactory.h"
+#include "modules/video_coding/codecs/vp8/include/vp8.h"
+
+namespace webrtc {
+
+// This class provides fallback to SimulcastEncoderAdapter if default VP8Encoder
+// doesn't support simulcast for provided settings.
+class VP8EncoderSimulcastProxy : public VP8Encoder {
+ public:
+  explicit VP8EncoderSimulcastProxy(
+      cricket::WebRtcVideoEncoderFactory* factory);
+  virtual ~VP8EncoderSimulcastProxy();
+
+  // Implements VideoEncoder.
+  int Release() override;
+  int InitEncode(const VideoCodec* inst,
+                 int number_of_cores,
+                 size_t max_payload_size) override;
+  int Encode(const VideoFrame& input_image,
+             const CodecSpecificInfo* codec_specific_info,
+             const std::vector<FrameType>* frame_types) override;
+  int RegisterEncodeCompleteCallback(EncodedImageCallback* callback) override;
+  int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
+  int SetRateAllocation(const BitrateAllocation& bitrate,
+                        uint32_t new_framerate) override;
+
+  VideoEncoder::ScalingSettings GetScalingSettings() const override;
+
+  int32_t SetPeriodicKeyFrames(bool enable) override;
+  bool SupportsNativeHandle() const override;
+  const char* ImplementationName() const override;
+
+ private:
+  cricket::WebRtcVideoEncoderFactory* const factory_;
+  std::unique_ptr<VideoEncoder> encoder_;
+  EncodedImageCallback* callback_;
+};
+
+}  // namespace webrtc
+
+#endif  // MEDIA_ENGINE_VP8_ENCODER_SIMULCAST_PROXY_H_
diff --git a/media/engine/vp8_encoder_simulcast_proxy_unittest.cc b/media/engine/vp8_encoder_simulcast_proxy_unittest.cc
new file mode 100644
index 0000000..0b26bb8
--- /dev/null
+++ b/media/engine/vp8_encoder_simulcast_proxy_unittest.cc
@@ -0,0 +1,152 @@
+/*
+ *  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 "media/engine/vp8_encoder_simulcast_proxy.h"
+
+#include <string>
+
+#include "media/engine/webrtcvideoencoderfactory.h"
+#include "modules/video_coding/codecs/vp8/temporal_layers.h"
+#include "modules/video_coding/include/video_codec_interface.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/video_codec_settings.h"
+
+namespace webrtc {
+namespace testing {
+
+using ::testing::Return;
+using ::testing::_;
+using ::testing::NiceMock;
+
+class MockEncoder : public VideoEncoder {
+ public:
+  // TODO(nisse): Valid overrides commented out, because the gmock
+  // methods don't use any override declarations, and we want to avoid
+  // warnings from -Winconsistent-missing-override. See
+  // http://crbug.com/428099.
+  MockEncoder() {}
+  virtual ~MockEncoder() {}
+
+  MOCK_METHOD3(InitEncode,
+               int32_t(const VideoCodec* codec_settings,
+                       int32_t number_of_cores,
+                       size_t max_payload_size));
+
+  MOCK_METHOD1(RegisterEncodeCompleteCallback, int32_t(EncodedImageCallback*));
+
+  MOCK_METHOD0(Release, int32_t());
+
+  MOCK_METHOD3(
+      Encode,
+      int32_t(const VideoFrame& inputImage,
+              const CodecSpecificInfo* codecSpecificInfo,
+              const std::vector<FrameType>* frame_types) /* override */);
+
+  MOCK_METHOD2(SetChannelParameters, int32_t(uint32_t packetLoss, int64_t rtt));
+
+  MOCK_CONST_METHOD0(ImplementationName, const char*());
+};
+
+class MockWebRtcVideoEncoderFactory
+    : public cricket::WebRtcVideoEncoderFactory {
+ public:
+  MockWebRtcVideoEncoderFactory() {}
+  virtual ~MockWebRtcVideoEncoderFactory() {}
+
+  MOCK_METHOD1(CreateVideoEncoder,
+               webrtc::VideoEncoder*(const cricket::VideoCodec& codec));
+
+  MOCK_CONST_METHOD0(supported_codecs, std::vector<cricket::VideoCodec>&());
+
+  MOCK_METHOD1(DestroyVideoEncoder, void(webrtc::VideoEncoder*));
+};
+
+TEST(VP8EncoderSimulcastProxy, ChoosesCorrectImplementation) {
+  const std::string kImplementationName = "Fake";
+  const std::string kSimulcastAdaptedImplementationName =
+      "SimulcastEncoderAdapter (Fake, Fake, Fake)";
+  VideoCodec codec_settings;
+  webrtc::test::CodecSettings(kVideoCodecVP8, &codec_settings);
+  TemporalLayersFactory tl_factory;
+  codec_settings.VP8()->tl_factory = &tl_factory;
+  codec_settings.simulcastStream[0] = {
+      test::kTestWidth, test::kTestHeight, 2, 2000, 1000, 1000, 56};
+  codec_settings.simulcastStream[1] = {
+      test::kTestWidth, test::kTestHeight, 2, 3000, 1000, 1000, 56};
+  codec_settings.simulcastStream[2] = {
+      test::kTestWidth, test::kTestHeight, 2, 5000, 1000, 1000, 56};
+  codec_settings.numberOfSimulcastStreams = 3;
+
+  NiceMock<MockEncoder> mock_encoder;
+  NiceMock<MockWebRtcVideoEncoderFactory> simulcast_factory;
+
+  EXPECT_CALL(mock_encoder, InitEncode(_, _, _))
+      .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK));
+  EXPECT_CALL(mock_encoder, ImplementationName())
+      .WillRepeatedly(Return(kImplementationName.c_str()));
+
+  EXPECT_CALL(simulcast_factory, CreateVideoEncoder(_))
+      .Times(1)
+      .WillOnce(Return(&mock_encoder));
+
+  VP8EncoderSimulcastProxy simulcast_enabled_proxy(&simulcast_factory);
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+            simulcast_enabled_proxy.InitEncode(&codec_settings, 4, 1200));
+  EXPECT_EQ(kImplementationName, simulcast_enabled_proxy.ImplementationName());
+
+  NiceMock<MockEncoder> mock_encoder1;
+  NiceMock<MockEncoder> mock_encoder2;
+  NiceMock<MockEncoder> mock_encoder3;
+  NiceMock<MockEncoder> mock_encoder4;
+  NiceMock<MockWebRtcVideoEncoderFactory> nonsimulcast_factory;
+
+  EXPECT_CALL(mock_encoder1, InitEncode(_, _, _))
+      .WillOnce(
+          Return(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED));
+  EXPECT_CALL(mock_encoder1, ImplementationName())
+      .WillRepeatedly(Return(kImplementationName.c_str()));
+
+  EXPECT_CALL(mock_encoder2, InitEncode(_, _, _))
+      .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK));
+  EXPECT_CALL(mock_encoder2, ImplementationName())
+      .WillRepeatedly(Return(kImplementationName.c_str()));
+
+  EXPECT_CALL(mock_encoder3, InitEncode(_, _, _))
+      .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK));
+  EXPECT_CALL(mock_encoder3, ImplementationName())
+      .WillRepeatedly(Return(kImplementationName.c_str()));
+
+  EXPECT_CALL(mock_encoder4, InitEncode(_, _, _))
+      .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK));
+  EXPECT_CALL(mock_encoder4, ImplementationName())
+      .WillRepeatedly(Return(kImplementationName.c_str()));
+
+  EXPECT_CALL(nonsimulcast_factory, CreateVideoEncoder(_))
+      .Times(4)
+      .WillOnce(Return(&mock_encoder1))
+      .WillOnce(Return(&mock_encoder2))
+      .WillOnce(Return(&mock_encoder3))
+      .WillOnce(Return(&mock_encoder4));
+
+  VP8EncoderSimulcastProxy simulcast_disabled_proxy(&nonsimulcast_factory);
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+            simulcast_disabled_proxy.InitEncode(&codec_settings, 4, 1200));
+  EXPECT_EQ(kSimulcastAdaptedImplementationName,
+            simulcast_disabled_proxy.ImplementationName());
+
+  // Cleanup.
+  simulcast_enabled_proxy.Release();
+  simulcast_disabled_proxy.Release();
+}
+
+}  // namespace testing
+}  // namespace webrtc
diff --git a/media/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc
index 80b4507..f5c6a29 100644
--- a/media/engine/webrtcvideoengine.cc
+++ b/media/engine/webrtcvideoengine.cc
@@ -42,6 +42,7 @@
 #include "rtc_base/timeutils.h"
 #include "rtc_base/trace_event.h"
 #include "system_wrappers/include/field_trial.h"
+#include "vp8_encoder_simulcast_proxy.h"
 
 using DegradationPreference = webrtc::VideoSendStream::DegradationPreference;
 
@@ -67,8 +68,7 @@
   virtual ~EncoderFactoryAdapter() {}
 
   virtual AllocatedEncoder CreateVideoEncoder(
-      const VideoCodec& codec,
-      bool is_conference_mode_screenshare) const = 0;
+      const VideoCodec& codec) const = 0;
 
   virtual std::vector<VideoCodec> GetSupportedCodecs() const = 0;
 };
@@ -99,9 +99,7 @@
         external_encoder_factory_(std::move(external_encoder_factory)) {}
 
  private:
-  AllocatedEncoder CreateVideoEncoder(
-      const VideoCodec& codec,
-      bool is_conference_mode_screenshare) const override;
+  AllocatedEncoder CreateVideoEncoder(const VideoCodec& codec) const override;
 
   std::vector<VideoCodec> GetSupportedCodecs() const override;
 
@@ -134,9 +132,7 @@
       : encoder_factory_(std::move(encoder_factory)) {}
 
  private:
-  AllocatedEncoder CreateVideoEncoder(
-      const VideoCodec& codec,
-      bool is_conference_mode_screenshare) const override {
+  AllocatedEncoder CreateVideoEncoder(const VideoCodec& codec) const override {
     if (!encoder_factory_)
       return AllocatedEncoder();
     const webrtc::SdpVideoFormat format(codec.name, codec.params);
@@ -1688,8 +1684,7 @@
 
 EncoderFactoryAdapter::AllocatedEncoder
 CricketEncoderFactoryAdapter::CreateVideoEncoder(
-    const VideoCodec& codec,
-    bool is_conference_mode_screenshare) const {
+    const VideoCodec& codec) const {
   // Try creating external encoder.
   if (external_encoder_factory_ != nullptr &&
       FindMatchingCodec(external_encoder_factory_->supported_codecs(), codec)) {
@@ -1719,12 +1714,10 @@
   // Try creating internal encoder.
   std::unique_ptr<webrtc::VideoEncoder> internal_encoder;
   if (FindMatchingCodec(internal_encoder_factory_->supported_codecs(), codec)) {
-    if (CodecNamesEq(codec.name.c_str(), kVp8CodecName) &&
-        is_conference_mode_screenshare && UseSimulcastScreenshare()) {
-      // TODO(sprang): Remove this adapter once libvpx supports simulcast with
-      // same-resolution substreams.
+    if (CodecNamesEq(codec.name.c_str(), kVp8CodecName)) {
       internal_encoder = std::unique_ptr<webrtc::VideoEncoder>(
-          new webrtc::SimulcastEncoderAdapter(internal_encoder_factory_.get()));
+          new webrtc::VP8EncoderSimulcastProxy(
+              internal_encoder_factory_.get()));
     } else {
       internal_encoder = std::unique_ptr<webrtc::VideoEncoder>(
           internal_encoder_factory_->CreateVideoEncoder(codec));
@@ -1753,13 +1746,8 @@
   std::unique_ptr<webrtc::VideoEncoder> new_encoder;
   if (force_encoder_allocation || !allocated_encoder_ ||
       allocated_codec_ != codec_settings.codec) {
-    const bool is_conference_mode_screenshare =
-        parameters_.encoder_config.content_type ==
-            webrtc::VideoEncoderConfig::ContentType::kScreen &&
-        parameters_.conference_mode;
     EncoderFactoryAdapter::AllocatedEncoder new_allocated_encoder =
-        encoder_factory_->CreateVideoEncoder(codec_settings.codec,
-                                             is_conference_mode_screenshare);
+        encoder_factory_->CreateVideoEncoder(codec_settings.codec);
     new_encoder = std::unique_ptr<webrtc::VideoEncoder>(
         std::move(new_allocated_encoder.encoder));
     parameters_.config.encoder_settings.encoder = new_encoder.get();
diff --git a/modules/video_coding/codecs/vp8/simulcast_unittest.cc b/modules/video_coding/codecs/vp8/simulcast_unittest.cc
index d8b44f4..d6b6dc5 100644
--- a/modules/video_coding/codecs/vp8/simulcast_unittest.cc
+++ b/modules/video_coding/codecs/vp8/simulcast_unittest.cc
@@ -67,10 +67,6 @@
   TestVp8Simulcast::TestSaptioTemporalLayers333PatternEncoder();
 }
 
-TEST_F(TestVp8Impl, TestSpatioTemporalLayers321PatternEncoder) {
-  TestVp8Simulcast::TestSpatioTemporalLayers321PatternEncoder();
-}
-
 TEST_F(TestVp8Impl, TestStrideEncodeDecode) {
   TestVp8Simulcast::TestStrideEncodeDecode();
 }
diff --git a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc
index f6a266a..7214d53 100644
--- a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc
+++ b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc
@@ -286,6 +286,64 @@
   EXPECT_EQ(encoded_cb_.encoded_frame_.qp_, *decoded_cb_.qp_);
 }
 
+TEST_F(TestVp8Impl, ChecksSimulcastSettings) {
+  codec_settings_.numberOfSimulcastStreams = 2;
+  // Reslutions are not scaled by 2, temporal layers do not match.
+  codec_settings_.simulcastStream[0] = {kWidth, kHeight, 2, 4000,
+                                        3000,   2000,    80};
+  codec_settings_.simulcastStream[1] = {kWidth, kHeight, 3, 4000,
+                                        3000,   2000,    80};
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED,
+            encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
+  codec_settings_.numberOfSimulcastStreams = 3;
+  // Reslutions are not scaled by 2.
+  codec_settings_.simulcastStream[0] = {kWidth / 2, kHeight / 2, 1, 4000,
+                                        3000,       2000,        80};
+  codec_settings_.simulcastStream[1] = {kWidth / 2, kHeight / 2, 1, 4000,
+                                        3000,       2000,        80};
+  codec_settings_.simulcastStream[2] = {kWidth, kHeight, 1, 4000,
+                                        3000,   2000,    80};
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED,
+            encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
+  // Reslutions are not scaled by 2.
+  codec_settings_.simulcastStream[0] = {kWidth, kHeight, 1, 4000,
+                                        3000,   2000,    80};
+  codec_settings_.simulcastStream[1] = {kWidth, kHeight, 1, 4000,
+                                        3000,   2000,    80};
+  codec_settings_.simulcastStream[2] = {kWidth, kHeight, 1, 4000,
+                                        3000,   2000,    80};
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED,
+            encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
+  // Temporal layers do not match.
+  codec_settings_.simulcastStream[0] = {kWidth / 4, kHeight / 4, 1, 4000,
+                                        3000,       2000,        80};
+  codec_settings_.simulcastStream[1] = {kWidth / 2, kHeight / 2, 2, 4000,
+                                        3000,       2000,        80};
+  codec_settings_.simulcastStream[2] = {kWidth, kHeight, 3, 4000,
+                                        3000,   2000,    80};
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED,
+            encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
+  // Resolutions do not match codec config.
+  codec_settings_.simulcastStream[0] = {
+      kWidth / 4 + 1, kHeight / 4 + 1, 1, 4000, 3000, 2000, 80};
+  codec_settings_.simulcastStream[1] = {
+      kWidth / 2 + 2, kHeight / 2 + 2, 1, 4000, 3000, 2000, 80};
+  codec_settings_.simulcastStream[2] = {kWidth + 4, kHeight + 4, 1, 4000,
+                                        3000,       2000,        80};
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED,
+            encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
+  // Everything fine: scaling by 2, top resolution matches video, temporal
+  // settings are the same for all layers.
+  codec_settings_.simulcastStream[0] = {kWidth / 4, kHeight / 4, 1, 4000,
+                                        3000,       2000,        80};
+  codec_settings_.simulcastStream[1] = {kWidth / 2, kHeight / 2, 1, 4000,
+                                        3000,       2000,        80};
+  codec_settings_.simulcastStream[2] = {kWidth, kHeight, 1, 4000,
+                                        3000,   2000,    80};
+  EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+            encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize));
+}
+
 #if defined(WEBRTC_ANDROID)
 #define MAYBE_AlignedStrideEncodeDecode DISABLED_AlignedStrideEncodeDecode
 #else
diff --git a/modules/video_coding/codecs/vp8/vp8_impl.cc b/modules/video_coding/codecs/vp8/vp8_impl.cc
index 53c198b..b97ef22 100644
--- a/modules/video_coding/codecs/vp8/vp8_impl.cc
+++ b/modules/video_coding/codecs/vp8/vp8_impl.cc
@@ -100,6 +100,21 @@
       return false;
     }
   }
+  for (int i = 1; i < num_streams; ++i) {
+    if (codec.simulcastStream[i].width !=
+        codec.simulcastStream[i - 1].width * 2) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool ValidSimulcastTemporalLayers(const VideoCodec& codec, int num_streams) {
+  for (int i = 0; i < num_streams - 1; ++i) {
+    if (codec.simulcastStream[i].numberOfTemporalLayers !=
+        codec.simulcastStream[i + 1].numberOfTemporalLayers)
+      return false;
+  }
   return true;
 }
 
@@ -395,8 +410,10 @@
   int number_of_streams = NumberOfStreams(*inst);
   bool doing_simulcast = (number_of_streams > 1);
 
-  if (doing_simulcast && !ValidSimulcastResolutions(*inst, number_of_streams)) {
-    return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
+  if (doing_simulcast &&
+      (!ValidSimulcastResolutions(*inst, number_of_streams) ||
+       !ValidSimulcastTemporalLayers(*inst, number_of_streams))) {
+    return WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED;
   }
 
   int num_temporal_layers =
diff --git a/modules/video_coding/include/video_error_codes.h b/modules/video_coding/include/video_error_codes.h
index e40c8b2..40e5a9a 100644
--- a/modules/video_coding/include/video_error_codes.h
+++ b/modules/video_coding/include/video_error_codes.h
@@ -28,5 +28,6 @@
 #define WEBRTC_VIDEO_CODEC_ERR_REQUEST_SLI -12
 #define WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE -13
 #define WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT -14
+#define WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED -15
 
 #endif  // MODULES_VIDEO_CODING_INCLUDE_VIDEO_ERROR_CODES_H_