Move FakeCodec to separate target and behave like real encoder.

Add FakeVp8Encoder, change FakeEncoder to use BitrateAllocator for simulcast.
Change call_test to use VP8 payload name for simulcast tests.

Bug: none
Change-Id: I5a34c52e66bbd6c05859729ed14ae87ac26b5969
Reviewed-on: https://webrtc-review.googlesource.com/91861
Commit-Queue: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24359}
diff --git a/call/BUILD.gn b/call/BUILD.gn
index b512e09..ee06825 100644
--- a/call/BUILD.gn
+++ b/call/BUILD.gn
@@ -347,11 +347,13 @@
       "../system_wrappers",
       "../test:audio_codec_mocks",
       "../test:direct_transport",
+      "../test:fake_video_codecs",
       "../test:field_trial",
       "../test:test_common",
       "../test:test_support",
       "../test:video_test_common",
       "../video:video",
+      "//testing/gmock",
       "//testing/gtest",
       "//third_party/abseil-cpp/absl/memory",
     ]
@@ -390,6 +392,7 @@
       "../system_wrappers:metrics_default",
       "../system_wrappers:runtime_enabled_features_default",
       "../test:direct_transport",
+      "../test:fake_video_codecs",
       "../test:field_trial",
       "../test:fileutils",
       "../test:perf_test",
diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc
index 23116b2..2b98036 100644
--- a/call/rtp_video_sender.cc
+++ b/call/rtp_video_sender.cc
@@ -357,6 +357,9 @@
         // inactive.
         if (layer_bitrates[i]) {
           rtp_modules_[i]->SetVideoBitrateAllocation(*layer_bitrates[i]);
+        } else {
+          // Signal a 0 bitrate on a simulcast stream.
+          rtp_modules_[i]->SetVideoBitrateAllocation(VideoBitrateAllocation());
         }
       }
     }
diff --git a/modules/video_coding/utility/default_video_bitrate_allocator.cc b/modules/video_coding/utility/default_video_bitrate_allocator.cc
index a4a40dd..b9d20c5 100644
--- a/modules/video_coding/utility/default_video_bitrate_allocator.cc
+++ b/modules/video_coding/utility/default_video_bitrate_allocator.cc
@@ -12,6 +12,8 @@
 
 #include <stdint.h>
 
+#include <algorithm>
+
 namespace webrtc {
 
 DefaultVideoBitrateAllocator::DefaultVideoBitrateAllocator(
@@ -27,14 +29,23 @@
   if (total_bitrate_bps == 0 || !codec_.active)
     return allocation;
 
-  if (total_bitrate_bps < codec_.minBitrate * 1000) {
-    allocation.SetBitrate(0, 0, codec_.minBitrate * 1000);
-  } else if (codec_.maxBitrate > 0 &&
-             total_bitrate_bps > codec_.maxBitrate * 1000) {
-    allocation.SetBitrate(0, 0, codec_.maxBitrate * 1000);
-  } else {
-    allocation.SetBitrate(0, 0, total_bitrate_bps);
+  uint32_t allocated_bitrate_bps = total_bitrate_bps;
+  allocated_bitrate_bps =
+      std::max(allocated_bitrate_bps, codec_.minBitrate * 1000);
+  if (codec_.maxBitrate > 0) {
+    allocated_bitrate_bps =
+        std::min(allocated_bitrate_bps, codec_.maxBitrate * 1000);
   }
+  size_t num_simulcast_streams =
+      std::max<size_t>(1, codec_.numberOfSimulcastStreams);
+  // The bitrate is split between all the streams in proportion of powers of 2
+  // e.g. 1:2, 1:2:4, etc.
+  for (size_t i = 0; i < num_simulcast_streams; i++) {
+    allocation.SetBitrate(
+        i, 0,
+        allocated_bitrate_bps * (1 << i) / ((1 << num_simulcast_streams) - 1));
+  }
+
   return allocation;
 }
 
diff --git a/test/BUILD.gn b/test/BUILD.gn
index ac56bc6..b5ec01b 100644
--- a/test/BUILD.gn
+++ b/test/BUILD.gn
@@ -299,23 +299,32 @@
   rtc_test("test_support_unittests") {
     deps = [
       ":direct_transport",
+      ":fake_video_codecs",
       ":fileutils",
+      ":fileutils_unittests",
       ":perf_test",
       ":rtp_test_utils",
+      ":test_common",
       ":test_main",
       ":test_support",
       ":test_support_test_artifacts",
       ":video_test_common",
       ":video_test_support",
+      "../api:create_simulcast_test_fixture_api",
+      "../api:simulcast_test_fixture_api",
       "../api/video:video_frame_i420",
       "../modules/rtp_rtcp:rtp_rtcp",
+      "../modules/video_capture",
+      "../modules/video_coding:simulcast_test_fixture_impl",
       "../rtc_base:rtc_base_approved",
       "../test:single_threaded_task_queue",
+      "//testing/gmock",
       "//testing/gtest",
       "//third_party/abseil-cpp/absl/memory",
     ]
     sources = [
       "direct_transport_unittest.cc",
+      "fake_vp8_encoder_unittest.cc",
       "frame_generator_unittest.cc",
       "rtp_file_reader_unittest.cc",
       "rtp_file_writer_unittest.cc",
@@ -342,6 +351,7 @@
     if (is_ios) {
       deps += [ ":test_support_unittests_bundle_data" ]
     }
+
     if (!is_android && !build_with_chromium) {
       # This is needed in order to avoid:
       # undefined symbol: webrtc::videocapturemodule::VideoCaptureImpl::Create
@@ -488,13 +498,46 @@
   ]
 }
 
+rtc_source_set("fake_video_codecs") {
+  testonly = true
+  visibility = [ "*" ]
+  sources = [
+    "configurable_frame_size_encoder.cc",
+    "configurable_frame_size_encoder.h",
+    "fake_decoder.cc",
+    "fake_decoder.h",
+    "fake_encoder.cc",
+    "fake_encoder.h",
+    "fake_vp8_encoder.cc",
+    "fake_vp8_encoder.h",
+  ]
+  if (!build_with_chromium && is_clang) {
+    # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+    suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+  }
+  deps = [
+    "..:webrtc_common",
+    "../api/video:video_frame_i420",
+    "../api/video_codecs:video_codecs_api",
+    "../common_video:common_video",
+    "../modules/video_coding:video_codec_interface",
+    "../modules/video_coding:video_coding_utility",
+    "../modules/video_coding:webrtc_h264",
+    "../modules/video_coding:webrtc_vp8",
+    "../modules/video_coding:webrtc_vp9",
+    "../rtc_base:checks",
+    "../rtc_base:rtc_base_approved",
+    "../rtc_base:rtc_task_queue",
+    "../rtc_base:sequenced_task_checker",
+    "../system_wrappers",
+  ]
+}
+
 rtc_source_set("test_common") {
   testonly = true
   sources = [
     "call_test.cc",
     "call_test.h",
-    "configurable_frame_size_encoder.cc",
-    "configurable_frame_size_encoder.h",
     "constants.cc",
     "constants.h",
     "drifting_clock.cc",
@@ -502,10 +545,6 @@
     "encoder_proxy_factory.h",
     "encoder_settings.cc",
     "encoder_settings.h",
-    "fake_decoder.cc",
-    "fake_decoder.h",
-    "fake_encoder.cc",
-    "fake_encoder.h",
     "fake_videorenderer.h",
     "function_video_decoder_factory.h",
     "function_video_encoder_factory.h",
@@ -534,18 +573,17 @@
 
   deps = [
     ":direct_transport",
+    ":fake_video_codecs",
     ":fileutils",
     ":rtp_test_utils",
     ":test_support",
     ":video_test_common",
-    "..:webrtc_common",
     "../api:libjingle_peerconnection_api",
     "../api:simulated_network_api",
     "../api:transport_api",
     "../api/audio_codecs:builtin_audio_decoder_factory",
     "../api/audio_codecs:builtin_audio_encoder_factory",
     "../api/video:video_frame",
-    "../api/video:video_frame_i420",
     "../api/video_codecs:video_codecs_api",
     "../audio",
     "../call",
@@ -555,7 +593,6 @@
     "../call:simulated_network",
     "../call:simulated_packet_receiver",
     "../call:video_stream_api",
-    "../common_video",
     "../logging:rtc_event_log_api",
     "../logging:rtc_event_log_impl_base",
     "../media:rtc_internal_video_codecs",
@@ -569,7 +606,6 @@
     "../modules/rtp_rtcp",
     "../modules/rtp_rtcp:mock_rtp_rtcp",
     "../modules/rtp_rtcp:rtp_rtcp_format",
-    "../modules/video_coding:video_codec_interface",
     "../modules/video_coding:video_coding_utility",
     "../modules/video_coding:webrtc_h264",
     "../modules/video_coding:webrtc_multiplex",
@@ -577,8 +613,6 @@
     "../modules/video_coding:webrtc_vp9",
     "../rtc_base:checks",
     "../rtc_base:rtc_base_approved",
-    "../rtc_base:rtc_task_queue",
-    "../rtc_base:sequenced_task_checker",
     "../rtc_base/experiments:congestion_controller_experiment",
     "../system_wrappers",
     "../system_wrappers:field_trial_api",
diff --git a/test/call_test.cc b/test/call_test.cc
index bf6ce2a..3b609c3 100644
--- a/test/call_test.cc
+++ b/test/call_test.cc
@@ -43,9 +43,14 @@
       audio_send_stream_(nullptr),
       bbr_network_controller_factory_(new BbrNetworkControllerFactory()),
       fake_encoder_factory_([this]() {
-        auto encoder = absl::make_unique<test::FakeEncoder>(clock_);
-        encoder->SetMaxBitrate(fake_encoder_max_bitrate_);
-        return encoder;
+        std::unique_ptr<FakeEncoder> fake_encoder;
+        if (video_encoder_configs_[0].codec_type == kVideoCodecVP8) {
+          fake_encoder = absl::make_unique<FakeVP8Encoder>(clock_);
+        } else {
+          fake_encoder = absl::make_unique<FakeEncoder>(clock_);
+        }
+        fake_encoder->SetMaxBitrate(fake_encoder_max_bitrate_);
+        return fake_encoder;
       }),
       num_video_streams_(1),
       num_audio_streams_(0),
diff --git a/test/call_test.h b/test/call_test.h
index f6c7e7f..eb96cfd 100644
--- a/test/call_test.h
+++ b/test/call_test.h
@@ -20,6 +20,7 @@
 #include "test/encoder_settings.h"
 #include "test/fake_decoder.h"
 #include "test/fake_videorenderer.h"
+#include "test/fake_vp8_encoder.h"
 #include "test/frame_generator_capturer.h"
 #include "test/function_video_encoder_factory.h"
 #include "test/rtp_rtcp_observer.h"
diff --git a/test/configurable_frame_size_encoder.cc b/test/configurable_frame_size_encoder.cc
index 7671a3c..c18c938 100644
--- a/test/configurable_frame_size_encoder.cc
+++ b/test/configurable_frame_size_encoder.cc
@@ -15,7 +15,6 @@
 #include "common_video/include/video_frame.h"
 #include "modules/video_coding/include/video_codec_interface.h"
 #include "rtc_base/checks.h"
-#include "test/gtest.h"
 
 namespace webrtc {
 namespace test {
diff --git a/test/configurable_frame_size_encoder.h b/test/configurable_frame_size_encoder.h
index b8c3b830d..4ad6749 100644
--- a/test/configurable_frame_size_encoder.h
+++ b/test/configurable_frame_size_encoder.h
@@ -22,7 +22,7 @@
 class ConfigurableFrameSizeEncoder : public VideoEncoder {
  public:
   explicit ConfigurableFrameSizeEncoder(size_t max_frame_size);
-  virtual ~ConfigurableFrameSizeEncoder();
+  ~ConfigurableFrameSizeEncoder() override;
 
   int32_t InitEncode(const VideoCodec* codec_settings,
                      int32_t number_of_cores,
diff --git a/test/fake_decoder.cc b/test/fake_decoder.cc
index 2155008..5b299ef 100644
--- a/test/fake_decoder.cc
+++ b/test/fake_decoder.cc
@@ -12,16 +12,17 @@
 
 #include "api/video/i420_buffer.h"
 #include "rtc_base/timeutils.h"
-#include "test/call_test.h"
-#include "test/gtest.h"
 
 namespace webrtc {
 namespace test {
 
+namespace {
+const int kDefaultWidth = 320;
+const int kDefaultHeight = 180;
+}  // namespace
+
 FakeDecoder::FakeDecoder()
-    : callback_(NULL),
-      width_(CallTest::kDefaultWidth),
-      height_(CallTest::kDefaultHeight) {}
+    : callback_(NULL), width_(kDefaultWidth), height_(kDefaultHeight) {}
 
 int32_t FakeDecoder::InitDecode(const VideoCodec* config,
                                 int32_t number_of_cores) {
@@ -75,7 +76,7 @@
       i += sizeof(kStartCode) + 1;  // Skip start code and NAL header.
     }
     if (input._buffer[i] != value) {
-      EXPECT_EQ(value, input._buffer[i])
+      RTC_CHECK_EQ(value, input._buffer[i])
           << "Bitstream mismatch between sender and receiver.";
       return -1;
     }
diff --git a/test/fake_encoder.cc b/test/fake_encoder.cc
index 5a3e2b8..1f90c29 100644
--- a/test/fake_encoder.cc
+++ b/test/fake_encoder.cc
@@ -16,10 +16,10 @@
 #include <memory>
 
 #include "common_types.h"  // NOLINT(build/include)
+#include "modules/video_coding/codecs/vp8/temporal_layers.h"
 #include "modules/video_coding/include/video_codec_interface.h"
 #include "rtc_base/checks.h"
 #include "system_wrappers/include/sleep.h"
-#include "test/gtest.h"
 
 namespace webrtc {
 namespace test {
@@ -37,6 +37,9 @@
   for (size_t i = 0; i < sizeof(encoded_buffer_); ++i) {
     encoded_buffer_[i] = static_cast<uint8_t>(i);
   }
+  for (bool& used : used_layers_) {
+    used = false;
+  }
 }
 
 void FakeEncoder::SetMaxBitrate(int max_kbps) {
@@ -53,6 +56,7 @@
   target_bitrate_.SetBitrate(0, 0, config_.startBitrate * 1000);
   configured_input_framerate_ = config_.maxFramerate;
   pending_keyframe_ = true;
+  last_frame_info_ = FrameInfo();
   return 0;
 }
 
@@ -63,9 +67,7 @@
   unsigned char num_simulcast_streams;
   SimulcastStream simulcast_streams[kMaxSimulcastStreams];
   EncodedImageCallback* callback;
-  uint32_t target_bitrate_sum_kbps;
-  int max_target_bitrate_kbps;
-  size_t num_encoded_bytes;
+  VideoBitrateAllocation target_bitrate;
   int framerate;
   VideoCodecMode mode;
   bool keyframe;
@@ -77,9 +79,7 @@
       simulcast_streams[i] = config_.simulcastStream[i];
     }
     callback = callback_;
-    target_bitrate_sum_kbps = target_bitrate_.get_sum_kbps();
-    max_target_bitrate_kbps = max_target_bitrate_kbps_;
-    num_encoded_bytes = sizeof(encoded_buffer_);
+    target_bitrate = target_bitrate_;
     mode = config_.mode;
     if (configured_input_framerate_ > 0) {
       framerate = configured_input_framerate_;
@@ -90,63 +90,28 @@
     pending_keyframe_ = false;
   }
 
-  for (FrameType frame_type : *frame_types) {
-    if (frame_type == kVideoFrameKey) {
-      keyframe = true;
-      break;
+  FrameInfo frame_info =
+      NextFrame(frame_types, keyframe, num_simulcast_streams, target_bitrate,
+                simulcast_streams, framerate);
+  for (uint8_t i = 0; i < frame_info.layers.size(); ++i) {
+    if (frame_info.layers[i].size == 0) {
+      // Drop this temporal layer.
+      continue;
     }
-  }
 
-  RTC_DCHECK_GT(max_framerate, 0);
-
-  size_t bitrate =
-      std::max(target_bitrate_sum_kbps, simulcast_streams[0].minBitrate);
-  if (max_target_bitrate_kbps > 0)
-    bitrate = std::min(bitrate, static_cast<size_t>(max_target_bitrate_kbps));
-
-  size_t bits_available = bitrate * 1000 / framerate;
-
-  RTC_DCHECK_GT(num_simulcast_streams, 0);
-  for (unsigned char i = 0; i < num_simulcast_streams; ++i) {
     CodecSpecificInfo specifics;
     memset(&specifics, 0, sizeof(specifics));
     specifics.codecType = kVideoCodecGeneric;
     specifics.codecSpecific.generic.simulcast_idx = i;
-    size_t min_stream_bits = static_cast<size_t>(
-        (simulcast_streams[i].minBitrate * 1000) / framerate);
-    size_t max_stream_bits = static_cast<size_t>(
-        (simulcast_streams[i].maxBitrate * 1000) / framerate);
-    size_t stream_bits =
-        (bits_available > max_stream_bits) ? max_stream_bits : bits_available;
-    size_t stream_bytes = (stream_bits + 7) / 8;
-    if (keyframe) {
-      // The first frame is a key frame and should be larger.
-      // Store the overshoot bytes and distribute them over the coming frames,
-      // so that we on average meet the bitrate target.
-      debt_bytes_ += (kKeyframeSizeFactor - 1) * stream_bytes;
-      stream_bytes *= kKeyframeSizeFactor;
-    } else {
-      if (debt_bytes_ > 0) {
-        // Pay at most half of the frame size for old debts.
-        size_t payment_size = std::min(stream_bytes / 2, debt_bytes_);
-        debt_bytes_ -= payment_size;
-        stream_bytes -= payment_size;
-      }
-    }
-
-    if (stream_bytes > num_encoded_bytes)
-      stream_bytes = num_encoded_bytes;
-
-    // Always encode something on the first frame.
-    if (min_stream_bits > bits_available && i > 0)
-      continue;
-
-    std::unique_ptr<uint8_t[]> encoded_buffer(new uint8_t[num_encoded_bytes]);
-    memcpy(encoded_buffer.get(), encoded_buffer_, num_encoded_bytes);
-    EncodedImage encoded(encoded_buffer.get(), stream_bytes, num_encoded_bytes);
-    encoded.SetTimestamp(input_image.timestamp());
+    std::unique_ptr<uint8_t[]> encoded_buffer(
+        new uint8_t[frame_info.layers[i].size]);
+    memcpy(encoded_buffer.get(), encoded_buffer_, frame_info.layers[i].size);
+    EncodedImage encoded(encoded_buffer.get(), frame_info.layers[i].size,
+                         sizeof(encoded_buffer_));
+    encoded._timeStamp = input_image.timestamp();
     encoded.capture_time_ms_ = input_image.render_time_ms();
-    encoded._frameType = (*frame_types)[i];
+    encoded._frameType =
+        frame_info.keyframe ? kVideoFrameKey : kVideoFrameDelta;
     encoded._encodedWidth = simulcast_streams[i].width;
     encoded._encodedHeight = simulcast_streams[i].height;
     encoded.rotation_ = input_image.rotation();
@@ -154,17 +119,77 @@
                                 ? VideoContentType::SCREENSHARE
                                 : VideoContentType::UNSPECIFIED;
     specifics.codec_name = ImplementationName();
-    specifics.codecSpecific.generic.simulcast_idx = i;
-    RTC_DCHECK(callback);
     if (callback->OnEncodedImage(encoded, &specifics, nullptr).error !=
         EncodedImageCallback::Result::OK) {
       return -1;
     }
-    bits_available -= std::min(encoded._length * 8, bits_available);
   }
   return 0;
 }
 
+FakeEncoder::FrameInfo FakeEncoder::NextFrame(
+    const std::vector<FrameType>* frame_types,
+    bool keyframe,
+    uint8_t num_simulcast_streams,
+    const VideoBitrateAllocation& target_bitrate,
+    SimulcastStream simulcast_streams[kMaxSimulcastStreams],
+    int framerate) {
+  FrameInfo frame_info;
+  frame_info.keyframe = keyframe;
+
+  if (frame_types) {
+    for (FrameType frame_type : *frame_types) {
+      if (frame_type == kVideoFrameKey) {
+        frame_info.keyframe = true;
+        break;
+      }
+    }
+  }
+
+  for (uint8_t i = 0; i < num_simulcast_streams; ++i) {
+    if (target_bitrate.GetBitrate(i, 0) > 0) {
+      int temporal_id = last_frame_info_.layers.size() > i
+                            ? ++last_frame_info_.layers[i].temporal_id %
+                                  simulcast_streams[i].numberOfTemporalLayers
+                            : 0;
+      frame_info.layers.emplace_back(0, temporal_id);
+    }
+  }
+
+  if (last_frame_info_.layers.size() < frame_info.layers.size()) {
+    // A new keyframe is needed since a new layer will be added.
+    frame_info.keyframe = true;
+  }
+
+  for (uint8_t i = 0; i < frame_info.layers.size(); ++i) {
+    FrameInfo::SpatialLayer& layer_info = frame_info.layers[i];
+    if (frame_info.keyframe) {
+      layer_info.temporal_id = 0;
+      size_t avg_frame_size =
+          (target_bitrate.GetBitrate(i, 0) + 7) / (8 * framerate);
+
+      // The first frame is a key frame and should be larger.
+      // Store the overshoot bytes and distribute them over the coming frames,
+      // so that we on average meet the bitrate target.
+      debt_bytes_ += (kKeyframeSizeFactor - 1) * avg_frame_size;
+      layer_info.size = kKeyframeSizeFactor * avg_frame_size;
+    } else {
+      size_t avg_frame_size =
+          (target_bitrate.GetBitrate(i, layer_info.temporal_id) + 7) /
+          (8 * framerate);
+      layer_info.size = avg_frame_size;
+      if (debt_bytes_ > 0) {
+        // Pay at most half of the frame size for old debts.
+        size_t payment_size = std::min(avg_frame_size / 2, debt_bytes_);
+        debt_bytes_ -= payment_size;
+        layer_info.size -= payment_size;
+      }
+    }
+  }
+  last_frame_info_ = frame_info;
+  return frame_info;
+}
+
 int32_t FakeEncoder::RegisterEncodeCompleteCallback(
     EncodedImageCallback* callback) {
   rtc::CritScope cs(&crit_sect_);
diff --git a/test/fake_encoder.h b/test/fake_encoder.h
index 071019a..67acebe 100644
--- a/test/fake_encoder.h
+++ b/test/fake_encoder.h
@@ -50,7 +50,30 @@
   static const char* kImplementationName;
 
  protected:
+  struct FrameInfo {
+    bool keyframe;
+    struct SpatialLayer {
+      SpatialLayer() = default;
+      SpatialLayer(int size, int temporal_id)
+          : size(size), temporal_id(temporal_id) {}
+      // Size of a current frame in the layer.
+      int size = 0;
+      // Temporal index of a current frame in the layer.
+      int temporal_id = 0;
+    };
+    std::vector<SpatialLayer> layers;
+  };
+
+  FrameInfo NextFrame(const std::vector<FrameType>* frame_types,
+                      bool keyframe,
+                      uint8_t num_simulcast_streams,
+                      const VideoBitrateAllocation& target_bitrate,
+                      SimulcastStream simulcast_streams[kMaxSimulcastStreams],
+                      int framerate);
+
+  FrameInfo last_frame_info_;
   Clock* const clock_;
+
   VideoCodec config_ RTC_GUARDED_BY(crit_sect_);
   EncodedImageCallback* callback_ RTC_GUARDED_BY(crit_sect_);
   VideoBitrateAllocation target_bitrate_ RTC_GUARDED_BY(crit_sect_);
@@ -60,6 +83,7 @@
   rtc::CriticalSection crit_sect_;
 
   uint8_t encoded_buffer_[100000];
+  bool used_layers_[kMaxSimulcastStreams];
 
   // Current byte debt to be payed over a number of frames.
   // The debt is acquired by keyframes overshooting the bitrate target.
diff --git a/test/fake_vp8_encoder.cc b/test/fake_vp8_encoder.cc
new file mode 100644
index 0000000..9d05297
--- /dev/null
+++ b/test/fake_vp8_encoder.cc
@@ -0,0 +1,118 @@
+/*
+ *  Copyright (c) 2018 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 "test/fake_vp8_encoder.h"
+
+#include "common_types.h"  // NOLINT(build/include)
+#include "modules/video_coding/codecs/vp8/temporal_layers.h"
+#include "modules/video_coding/include/video_codec_interface.h"
+#include "modules/video_coding/include/video_error_codes.h"
+#include "modules/video_coding/utility/simulcast_utility.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/random.h"
+#include "rtc_base/timeutils.h"
+
+namespace webrtc {
+
+namespace test {
+
+FakeVP8Encoder::FakeVP8Encoder(Clock* clock)
+    : FakeEncoder(clock), callback_(nullptr) {
+  FakeEncoder::RegisterEncodeCompleteCallback(this);
+  sequence_checker_.Detach();
+}
+
+int32_t FakeVP8Encoder::RegisterEncodeCompleteCallback(
+    EncodedImageCallback* callback) {
+  RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
+  callback_ = callback;
+  return 0;
+}
+
+int32_t FakeVP8Encoder::InitEncode(const VideoCodec* config,
+                                   int32_t number_of_cores,
+                                   size_t max_payload_size) {
+  RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
+  auto result =
+      FakeEncoder::InitEncode(config, number_of_cores, max_payload_size);
+  if (result != WEBRTC_VIDEO_CODEC_OK) {
+    return result;
+  }
+
+  int number_of_streams = SimulcastUtility::NumberOfSimulcastStreams(*config);
+  bool doing_simulcast = number_of_streams > 1;
+
+  int num_temporal_layers =
+      doing_simulcast ? config->simulcastStream[0].numberOfTemporalLayers
+                      : config->VP8().numberOfTemporalLayers;
+  RTC_DCHECK_GT(num_temporal_layers, 0);
+
+  SetupTemporalLayers(number_of_streams, num_temporal_layers, *config);
+
+  return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t FakeVP8Encoder::Release() {
+  auto result = FakeEncoder::Release();
+  sequence_checker_.Detach();
+  return result;
+}
+
+void FakeVP8Encoder::SetupTemporalLayers(int num_streams,
+                                         int num_temporal_layers,
+                                         const VideoCodec& codec) {
+  RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
+
+  temporal_layers_.clear();
+  for (int i = 0; i < num_streams; ++i) {
+    temporal_layers_.emplace_back(
+        TemporalLayers::CreateTemporalLayers(codec, i));
+  }
+}
+
+void FakeVP8Encoder::PopulateCodecSpecific(
+    CodecSpecificInfo* codec_specific,
+    const TemporalLayers::FrameConfig& tl_config,
+    FrameType frame_type,
+    int stream_idx,
+    uint32_t timestamp) {
+  RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
+  codec_specific->codecType = kVideoCodecVP8;
+  codec_specific->codec_name = ImplementationName();
+  CodecSpecificInfoVP8* vp8Info = &(codec_specific->codecSpecific.VP8);
+  vp8Info->simulcastIdx = stream_idx;
+  vp8Info->keyIdx = kNoKeyIdx;
+  vp8Info->nonReference = false;
+  temporal_layers_[stream_idx]->PopulateCodecSpecific(
+      frame_type == kVideoFrameKey, tl_config, vp8Info, timestamp);
+}
+
+EncodedImageCallback::Result FakeVP8Encoder::OnEncodedImage(
+    const EncodedImage& encoded_image,
+    const CodecSpecificInfo* codec_specific_info,
+    const RTPFragmentationHeader* fragments) {
+  RTC_DCHECK_CALLED_SEQUENTIALLY(&sequence_checker_);
+  uint8_t stream_idx = codec_specific_info->codecSpecific.generic.simulcast_idx;
+  CodecSpecificInfo overrided_specific_info;
+  TemporalLayers::FrameConfig tl_config =
+      temporal_layers_[stream_idx]->UpdateLayerConfig(encoded_image._timeStamp);
+  PopulateCodecSpecific(&overrided_specific_info, tl_config,
+                        encoded_image._frameType, stream_idx,
+                        encoded_image._timeStamp);
+  temporal_layers_[stream_idx]->FrameEncoded(encoded_image._timeStamp,
+                                             encoded_image._length, -1);
+
+  return callback_->OnEncodedImage(encoded_image, &overrided_specific_info,
+                                   fragments);
+}
+
+}  // namespace test
+}  // namespace webrtc
diff --git a/test/fake_vp8_encoder.h b/test/fake_vp8_encoder.h
new file mode 100644
index 0000000..407361b
--- /dev/null
+++ b/test/fake_vp8_encoder.h
@@ -0,0 +1,66 @@
+/*
+ *  Copyright (c) 2018 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 TEST_FAKE_VP8_ENCODER_H_
+#define TEST_FAKE_VP8_ENCODER_H_
+
+#include <memory>
+#include <vector>
+
+#include "modules/video_coding/codecs/vp8/temporal_layers.h"
+#include "test/fake_encoder.h"
+
+#include "rtc_base/criticalsection.h"
+#include "rtc_base/sequenced_task_checker.h"
+
+namespace webrtc {
+namespace test {
+
+class FakeVP8Encoder : public FakeEncoder, public EncodedImageCallback {
+ public:
+  explicit FakeVP8Encoder(Clock* clock);
+  virtual ~FakeVP8Encoder() = default;
+
+  int32_t RegisterEncodeCompleteCallback(
+      EncodedImageCallback* callback) override;
+
+  int32_t InitEncode(const VideoCodec* config,
+                     int32_t number_of_cores,
+                     size_t max_payload_size) override;
+
+  int32_t Release() override;
+
+  const char* ImplementationName() const override { return "FakeVp8Encoder"; }
+
+  Result OnEncodedImage(const EncodedImage& encodedImage,
+                        const CodecSpecificInfo* codecSpecificInfo,
+                        const RTPFragmentationHeader* fragments) override;
+
+ private:
+  void SetupTemporalLayers(int num_streams,
+                           int num_temporal_layers,
+                           const VideoCodec& codec);
+  void PopulateCodecSpecific(CodecSpecificInfo* codec_specific,
+                             const TemporalLayers::FrameConfig& tl_config,
+                             FrameType frame_type,
+                             int stream_idx,
+                             uint32_t timestamp);
+
+  rtc::SequencedTaskChecker sequence_checker_;
+  EncodedImageCallback* callback_ RTC_GUARDED_BY(sequence_checker_);
+
+  std::vector<std::unique_ptr<TemporalLayers>> temporal_layers_
+      RTC_GUARDED_BY(sequence_checker_);
+};
+
+}  // namespace test
+}  // namespace webrtc
+
+#endif  // TEST_FAKE_VP8_ENCODER_H_
diff --git a/test/fake_vp8_encoder_unittest.cc b/test/fake_vp8_encoder_unittest.cc
new file mode 100644
index 0000000..c79ba0c
--- /dev/null
+++ b/test/fake_vp8_encoder_unittest.cc
@@ -0,0 +1,103 @@
+/*
+ *  Copyright (c) 2018 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 <memory>
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "api/test/create_simulcast_test_fixture.h"
+#include "api/test/simulcast_test_fixture.h"
+#include "modules/video_coding/utility/simulcast_test_fixture_impl.h"
+#include "test/fake_decoder.h"
+#include "test/fake_vp8_encoder.h"
+#include "test/function_video_decoder_factory.h"
+#include "test/function_video_encoder_factory.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+
+std::unique_ptr<SimulcastTestFixture> CreateSpecificSimulcastTestFixture() {
+  std::unique_ptr<VideoEncoderFactory> encoder_factory =
+      absl::make_unique<FunctionVideoEncoderFactory>([]() {
+        return absl::make_unique<FakeVP8Encoder>(Clock::GetRealTimeClock());
+      });
+  std::unique_ptr<VideoDecoderFactory> decoder_factory =
+      absl::make_unique<FunctionVideoDecoderFactory>(
+          []() { return absl::make_unique<FakeDecoder>(); });
+  return CreateSimulcastTestFixture(std::move(encoder_factory),
+                                    std::move(decoder_factory),
+                                    SdpVideoFormat("VP8"));
+}
+}  // namespace
+
+TEST(TestFakeVp8Codec, TestKeyFrameRequestsOnAllStreams) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestKeyFrameRequestsOnAllStreams();
+}
+
+TEST(TestFakeVp8Codec, TestPaddingAllStreams) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestPaddingAllStreams();
+}
+
+TEST(TestFakeVp8Codec, TestPaddingTwoStreams) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestPaddingTwoStreams();
+}
+
+TEST(TestFakeVp8Codec, TestPaddingTwoStreamsOneMaxedOut) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestPaddingTwoStreamsOneMaxedOut();
+}
+
+TEST(TestFakeVp8Codec, TestPaddingOneStream) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestPaddingOneStream();
+}
+
+TEST(TestFakeVp8Codec, TestPaddingOneStreamTwoMaxedOut) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestPaddingOneStreamTwoMaxedOut();
+}
+
+TEST(TestFakeVp8Codec, TestSendAllStreams) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestSendAllStreams();
+}
+
+TEST(TestFakeVp8Codec, TestDisablingStreams) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestDisablingStreams();
+}
+
+TEST(TestFakeVp8Codec, TestSwitchingToOneStream) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestSwitchingToOneStream();
+}
+
+TEST(TestFakeVp8Codec, TestSwitchingToOneOddStream) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestSwitchingToOneOddStream();
+}
+
+TEST(TestFakeVp8Codec, TestSwitchingToOneSmallStream) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestSwitchingToOneSmallStream();
+}
+
+TEST(TestFakeVp8Codec, TestSpatioTemporalLayers333PatternEncoder) {
+  auto fixture = CreateSpecificSimulcastTestFixture();
+  fixture->TestSpatioTemporalLayers333PatternEncoder();
+}
+
+}  // namespace test
+}  // namespace webrtc
diff --git a/video/BUILD.gn b/video/BUILD.gn
index d2a52fb..d6e529f 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -361,6 +361,7 @@
       "../system_wrappers",
       "../system_wrappers:metrics_default",
       "../system_wrappers:runtime_enabled_features_default",
+      "../test:fake_video_codecs",
       "../test:field_trial",
       "../test:rtp_test_utils",
       "../test:run_test",
@@ -472,6 +473,7 @@
       "../system_wrappers:metrics_api",
       "../system_wrappers:metrics_default",
       "../test:direct_transport",
+      "../test:fake_video_codecs",
       "../test:field_trial",
       "../test:fileutils",
       "../test:perf_test",
diff --git a/video/end_to_end_tests/extended_reports_tests.cc b/video/end_to_end_tests/extended_reports_tests.cc
index 322a9c3..acffcd6 100644
--- a/video/end_to_end_tests/extended_reports_tests.cc
+++ b/video/end_to_end_tests/extended_reports_tests.cc
@@ -30,7 +30,12 @@
         sent_rtcp_rrtr_(0),
         sent_rtcp_target_bitrate_(false),
         sent_zero_rtcp_target_bitrate_(false),
-        sent_rtcp_dlrr_(0) {}
+        sent_rtcp_dlrr_(0),
+        send_transport_(nullptr) {
+    forward_transport_config_.link_capacity_kbps = 500;
+    forward_transport_config_.queue_delay_ms = 0;
+    forward_transport_config_.loss_percent = 0;
+  }
 
  private:
   // Receive stream should send RR packets (and RRTR packets if enabled).
@@ -56,6 +61,14 @@
     test::RtcpPacketParser parser;
     EXPECT_TRUE(parser.Parse(packet, length));
 
+    if (parser.sender_ssrc() == test::CallTest::kVideoSendSsrcs[1] &&
+        enable_zero_target_bitrate_) {
+      // Reduce bandwidth restriction to disable second stream after it was
+      // enabled for some time.
+      forward_transport_config_.link_capacity_kbps = 200;
+      send_transport_->SetConfig(forward_transport_config_);
+    }
+
     sent_rtcp_sr_ += parser.sender_report()->num_packets();
     EXPECT_LE(parser.xr()->num_packets(), 1);
     if (parser.xr()->num_packets() > 0) {
@@ -64,8 +77,12 @@
         ++sent_rtcp_dlrr_;
       if (parser.xr()->target_bitrate()) {
         sent_rtcp_target_bitrate_ = true;
-        for (const rtcp::TargetBitrate::BitrateItem& item :
-             parser.xr()->target_bitrate()->GetTargetBitrates()) {
+        auto target_bitrates =
+            parser.xr()->target_bitrate()->GetTargetBitrates();
+        if (target_bitrates.empty()) {
+          sent_zero_rtcp_target_bitrate_ = true;
+        }
+        for (const rtcp::TargetBitrate::BitrateItem& item : target_bitrates) {
           if (item.target_bitrate_kbps == 0) {
             sent_zero_rtcp_target_bitrate_ = true;
             break;
@@ -98,39 +115,20 @@
     return enable_zero_target_bitrate_ ? 2 : 1;
   }
 
-  // This test uses VideoStream settings different from the the default one
-  // implemented in DefaultVideoStreamFactory, so it implements its own
-  // VideoEncoderConfig::VideoStreamFactoryInterface which is created
-  // in ModifyVideoConfigs.
-  class ZeroTargetVideoStreamFactory
-      : public VideoEncoderConfig::VideoStreamFactoryInterface {
-   public:
-    ZeroTargetVideoStreamFactory() {}
-
-   private:
-    std::vector<VideoStream> CreateEncoderStreams(
-        int width,
-        int height,
-        const VideoEncoderConfig& encoder_config) override {
-      std::vector<VideoStream> streams =
-          test::CreateVideoStreams(width, height, encoder_config);
-      // Set one of the streams' target bitrates to zero to test that a
-      // bitrate of 0 can be signalled.
-      streams[encoder_config.number_of_streams - 1].min_bitrate_bps = 0;
-      streams[encoder_config.number_of_streams - 1].target_bitrate_bps = 0;
-      streams[encoder_config.number_of_streams - 1].max_bitrate_bps = 0;
-      return streams;
-    }
-  };
+  test::PacketTransport* CreateSendTransport(
+      test::SingleThreadedTaskQueueForTesting* task_queue,
+      Call* sender_call) {
+    send_transport_ = new test::PacketTransport(
+        task_queue, sender_call, this, test::PacketTransport::kSender,
+        test::CallTest::payload_type_map_, forward_transport_config_);
+    return send_transport_;
+  }
 
   void ModifyVideoConfigs(
       VideoSendStream::Config* send_config,
       std::vector<VideoReceiveStream::Config>* receive_configs,
       VideoEncoderConfig* encoder_config) override {
     if (enable_zero_target_bitrate_) {
-      encoder_config->video_stream_factory =
-          new rtc::RefCountedObject<ZeroTargetVideoStreamFactory>();
-
       // Configure VP8 to be able to use simulcast.
       send_config->rtp.payload_name = "VP8";
       encoder_config->codec_type = kVideoCodecVP8;
@@ -166,6 +164,8 @@
   bool sent_rtcp_target_bitrate_ RTC_GUARDED_BY(&crit_);
   bool sent_zero_rtcp_target_bitrate_ RTC_GUARDED_BY(&crit_);
   int sent_rtcp_dlrr_;
+  DefaultNetworkSimulationConfig forward_transport_config_;
+  test::PacketTransport* send_transport_;
 };
 
 TEST_F(ExtendedReportsEndToEndTest,