VideoReceiveStream: Enable encoded frame sink.

This change ultimately enables wiring up VideoRtpReceiver::OnGenerateKeyFrame and
OnEncodedSinkEnabled into internal::VideoReceiveStream so that encoded frames
can flow to sinks installed in VideoTrackSourceInterface.

Bug: chromium:1013590
Change-Id: I0779932c251a2159880a39b2d42d5ce439cc88e6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/161090
Commit-Queue: Markus Handell <handellm@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29988}
diff --git a/api/video/encoded_image.h b/api/video/encoded_image.h
index 1fa2b04..b375d48 100644
--- a/api/video/encoded_image.h
+++ b/api/video/encoded_image.h
@@ -153,6 +153,11 @@
     capacity_ = 0;
   }
 
+  rtc::scoped_refptr<EncodedImageBufferInterface> GetEncodedData() const {
+    RTC_DCHECK(buffer_ == nullptr);
+    return encoded_data_;
+  }
+
   // TODO(nisse): Delete, provide only read-only access to the buffer.
   uint8_t* data() {
     return buffer_ ? buffer_
diff --git a/call/BUILD.gn b/call/BUILD.gn
index 76e1b45..2a89f71 100644
--- a/call/BUILD.gn
+++ b/call/BUILD.gn
@@ -292,6 +292,7 @@
     "../api/crypto:frame_encryptor_interface",
     "../api/crypto:options",
     "../api/transport/rtp:rtp_source",
+    "../api/video:recordable_encoded_frame",
     "../api/video:video_frame",
     "../api/video:video_rtp_headers",
     "../api/video:video_stream_encoder",
diff --git a/call/video_receive_stream.h b/call/video_receive_stream.h
index 96c60b5..0f5e8e0 100644
--- a/call/video_receive_stream.h
+++ b/call/video_receive_stream.h
@@ -15,6 +15,7 @@
 #include <map>
 #include <set>
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "api/call/transport.h"
@@ -23,6 +24,7 @@
 #include "api/rtp_headers.h"
 #include "api/rtp_parameters.h"
 #include "api/transport/rtp/rtp_source.h"
+#include "api/video/recordable_encoded_frame.h"
 #include "api/video/video_content_type.h"
 #include "api/video/video_frame.h"
 #include "api/video/video_sink_interface.h"
@@ -39,6 +41,26 @@
 
 class VideoReceiveStream {
  public:
+  // Class for handling moving in/out recording state.
+  struct RecordingState {
+    RecordingState() = default;
+    explicit RecordingState(
+        std::function<void(const RecordableEncodedFrame&)> callback)
+        : callback(std::move(callback)) {}
+
+    // Callback stored from the VideoReceiveStream. The VideoReceiveStream
+    // client should not interpret the attribute.
+    std::function<void(const RecordableEncodedFrame&)> callback;
+    // Memento of internal state in VideoReceiveStream, recording wether
+    // we're currently causing generation of a keyframe from the sender. Needed
+    // to avoid sending double keyframe requests. The VideoReceiveStream client
+    // should not interpret the attribute.
+    bool keyframe_needed = false;
+    // Memento of when a keyframe request was last sent. The VideoReceiveStream
+    // client should not interpret the attribute.
+    absl::optional<int64_t> last_keyframe_request_ms;
+  };
+
   // TODO(mflodman) Move all these settings to VideoDecoder and move the
   // declaration to common_types.h.
   struct Decoder {
@@ -275,6 +297,21 @@
   virtual void SetFrameDecryptor(
       rtc::scoped_refptr<FrameDecryptorInterface> frame_decryptor) = 0;
 
+  // Sets and returns recording state. The old state is moved out
+  // of the video receive stream and returned to the caller, and |state|
+  // is moved in. If the state's callback is set, it will be called with
+  // recordable encoded frames as they arrive.
+  // If |generate_key_frame| is true, the method will generate a key frame.
+  // When the function returns, it's guaranteed that all old callouts
+  // to the returned callback has ceased.
+  // Note: the client should not interpret the returned state's attributes, but
+  // instead treat it as opaque data.
+  virtual RecordingState SetAndGetRecordingState(RecordingState state,
+                                                 bool generate_key_frame) = 0;
+
+  // Cause eventual generation of a key frame from the sender.
+  virtual void GenerateKeyFrame() = 0;
+
  protected:
   virtual ~VideoReceiveStream() {}
 };
diff --git a/media/engine/fake_webrtc_call.h b/media/engine/fake_webrtc_call.h
index 9441e99..5179323 100644
--- a/media/engine/fake_webrtc_call.h
+++ b/media/engine/fake_webrtc_call.h
@@ -229,6 +229,12 @@
   void SetFrameDecryptor(rtc::scoped_refptr<webrtc::FrameDecryptorInterface>
                              frame_decryptor) override {}
 
+  RecordingState SetAndGetRecordingState(RecordingState state,
+                                         bool generate_key_frame) override {
+    return RecordingState();
+  }
+  void GenerateKeyFrame() override {}
+
  private:
   // webrtc::VideoReceiveStream implementation.
   void Start() override;
diff --git a/modules/video_coding/encoded_frame.h b/modules/video_coding/encoded_frame.h
index 028c19b..798c005 100644
--- a/modules/video_coding/encoded_frame.h
+++ b/modules/video_coding/encoded_frame.h
@@ -54,7 +54,9 @@
 
   using EncodedImage::ColorSpace;
   using EncodedImage::data;
+  using EncodedImage::GetEncodedData;
   using EncodedImage::PacketInfos;
+  using EncodedImage::Retain;
   using EncodedImage::set_size;
   using EncodedImage::SetColorSpace;
   using EncodedImage::SetEncodedData;
@@ -76,6 +78,12 @@
    */
   webrtc::VideoFrameType FrameType() const { return _frameType; }
   /**
+   *   Set frame type
+   */
+  void SetFrameType(webrtc::VideoFrameType frame_type) {
+    _frameType = frame_type;
+  }
+  /**
    *   Get frame rotation
    */
   VideoRotation rotation() const { return rotation_; }
diff --git a/video/BUILD.gn b/video/BUILD.gn
index bb54fe8..68cee87 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -64,6 +64,7 @@
     "../api/task_queue",
     "../api/transport/media:media_transport_interface",
     "../api/video:encoded_image",
+    "../api/video:recordable_encoded_frame",
     "../api/video:video_bitrate_allocation",
     "../api/video:video_bitrate_allocator",
     "../api/video:video_codec_constants",
@@ -614,6 +615,7 @@
       "../modules/utility",
       "../modules/video_coding",
       "../modules/video_coding:codec_globals_headers",
+      "../modules/video_coding:encoded_frame",
       "../modules/video_coding:video_codec_interface",
       "../modules/video_coding:video_coding_utility",
       "../modules/video_coding:webrtc_h264",
@@ -645,6 +647,7 @@
       "../test:test_common",
       "../test:test_support",
       "../test:video_test_common",
+      "../test/time_controller:time_controller",
       "//testing/gtest",
       "//third_party/abseil-cpp/absl/algorithm:container",
       "//third_party/abseil-cpp/absl/memory",
diff --git a/video/video_receive_stream.cc b/video/video_receive_stream.cc
index 8213c64..7f68f76 100644
--- a/video/video_receive_stream.cc
+++ b/video/video_receive_stream.cc
@@ -54,6 +54,10 @@
 
 namespace webrtc {
 
+namespace internal {
+constexpr int VideoReceiveStream::kMaxWaitForKeyFrameMs;
+}  // namespace internal
+
 namespace {
 
 using video_coding::EncodedFrame;
@@ -62,9 +66,53 @@
 constexpr int kMinBaseMinimumDelayMs = 0;
 constexpr int kMaxBaseMinimumDelayMs = 10000;
 
-constexpr int kMaxWaitForKeyFrameMs = 200;
 constexpr int kMaxWaitForFrameMs = 3000;
 
+// Concrete instance of RecordableEncodedFrame wrapping needed content
+// from video_coding::EncodedFrame.
+class WebRtcRecordableEncodedFrame : public RecordableEncodedFrame {
+ public:
+  explicit WebRtcRecordableEncodedFrame(const EncodedFrame& frame)
+      : buffer_(frame.GetEncodedData()),
+        render_time_ms_(frame.RenderTime()),
+        codec_(frame.CodecSpecific()->codecType),
+        is_key_frame_(frame.FrameType() == VideoFrameType::kVideoFrameKey),
+        resolution_{frame.EncodedImage()._encodedWidth,
+                    frame.EncodedImage()._encodedHeight} {
+    if (frame.ColorSpace()) {
+      color_space_ = *frame.ColorSpace();
+    }
+  }
+
+  // VideoEncodedSinkInterface::FrameBuffer
+  rtc::scoped_refptr<const EncodedImageBufferInterface> encoded_buffer()
+      const override {
+    return buffer_;
+  }
+
+  absl::optional<webrtc::ColorSpace> color_space() const override {
+    return color_space_;
+  }
+
+  VideoCodecType codec() const override { return codec_; }
+
+  bool is_key_frame() const override { return is_key_frame_; }
+
+  EncodedResolution resolution() const override { return resolution_; }
+
+  Timestamp render_time() const override {
+    return Timestamp::ms(render_time_ms_);
+  }
+
+ private:
+  rtc::scoped_refptr<EncodedImageBufferInterface> buffer_;
+  int64_t render_time_ms_;
+  VideoCodecType codec_;
+  bool is_key_frame_;
+  EncodedResolution resolution_;
+  absl::optional<webrtc::ColorSpace> color_space_;
+};
+
 VideoCodec CreateDecoderVideoCodec(const VideoReceiveStream::Decoder& decoder) {
   VideoCodec codec;
   memset(&codec, 0, sizeof(codec));
@@ -501,7 +549,7 @@
     std::unique_ptr<video_coding::EncodedFrame> frame) {
   RTC_DCHECK_RUN_ON(&network_sequence_checker_);
   // TODO(https://bugs.webrtc.org/9974): Consider removing this workaround.
-  int64_t time_now_ms = rtc::TimeMillis();
+  int64_t time_now_ms = clock_->TimeInMilliseconds();
   if (last_complete_frame_time_ms_ > 0 &&
       time_now_ms - last_complete_frame_time_ms_ > kInactiveStreamThresholdMs) {
     frame_buffer_->Clear();
@@ -607,7 +655,8 @@
     }
   }
   stats_proxy_.OnPreDecode(frame->CodecSpecific()->codecType, qp);
-
+  HandleKeyFrameGeneration(frame->FrameType() == VideoFrameType::kVideoFrameKey,
+                           now_ms);
   int decode_result = video_receiver_.Decode(frame.get());
   if (decode_result == WEBRTC_VIDEO_CODEC_OK ||
       decode_result == WEBRTC_VIDEO_CODEC_OK_REQUEST_KEYFRAME) {
@@ -624,14 +673,35 @@
     //                 has been fixed.
     RequestKeyFrame(now_ms);
   }
+
+  if (encoded_frame_buffer_function_) {
+    frame->Retain();
+    encoded_frame_buffer_function_(WebRtcRecordableEncodedFrame(*frame));
+  }
+}
+
+void VideoReceiveStream::HandleKeyFrameGeneration(
+    bool received_frame_is_keyframe,
+    int64_t now_ms) {
+  // Repeat sending keyframe requests if we've requested a keyframe.
+  if (!keyframe_generation_requested_) {
+    return;
+  }
+  if (received_frame_is_keyframe) {
+    keyframe_generation_requested_ = false;
+  } else if (last_keyframe_request_ms_ + max_wait_for_keyframe_ms_ <= now_ms) {
+    if (!IsReceivingKeyFrame(now_ms)) {
+      RequestKeyFrame(now_ms);
+    }
+  } else {
+    // It hasn't been long enough since the last keyframe request, do nothing.
+  }
 }
 
 void VideoReceiveStream::HandleFrameBufferTimeout() {
   int64_t now_ms = clock_->TimeInMilliseconds();
   absl::optional<int64_t> last_packet_ms =
       rtp_video_stream_receiver_.LastReceivedPacketMs();
-  absl::optional<int64_t> last_keyframe_packet_ms =
-      rtp_video_stream_receiver_.LastReceivedKeyframePacketMs();
 
   // To avoid spamming keyframe requests for a stream that is not active we
   // check if we have received a packet within the last 5 seconds.
@@ -639,13 +709,7 @@
   if (!stream_is_active)
     stats_proxy_.OnStreamInactive();
 
-  // If we recently have been receiving packets belonging to a keyframe then
-  // we assume a keyframe is currently being received.
-  bool receiving_keyframe =
-      last_keyframe_packet_ms &&
-      now_ms - *last_keyframe_packet_ms < max_wait_for_keyframe_ms_;
-
-  if (stream_is_active && !receiving_keyframe &&
+  if (stream_is_active && !IsReceivingKeyFrame(now_ms) &&
       (!config_.crypto_options.sframe.require_frame_encryption ||
        rtp_video_stream_receiver_.IsDecryptable())) {
     RTC_LOG(LS_WARNING) << "No decodable frame in " << GetWaitMs()
@@ -654,6 +718,18 @@
   }
 }
 
+bool VideoReceiveStream::IsReceivingKeyFrame(int64_t timestamp_ms) const {
+  absl::optional<int64_t> last_keyframe_packet_ms =
+      rtp_video_stream_receiver_.LastReceivedKeyframePacketMs();
+
+  // If we recently have been receiving packets belonging to a keyframe then
+  // we assume a keyframe is currently being received.
+  bool receiving_keyframe =
+      last_keyframe_packet_ms &&
+      timestamp_ms - *last_keyframe_packet_ms < max_wait_for_keyframe_ms_;
+  return receiving_keyframe;
+}
+
 void VideoReceiveStream::UpdatePlayoutDelays() const {
   const int minimum_delay_ms =
       std::max({frame_minimum_playout_delay_ms_, base_minimum_playout_delay_ms_,
@@ -672,5 +748,42 @@
   return source_tracker_.GetSources();
 }
 
+VideoReceiveStream::RecordingState VideoReceiveStream::SetAndGetRecordingState(
+    RecordingState state,
+    bool generate_key_frame) {
+  RTC_DCHECK_RUN_ON(&worker_sequence_checker_);
+  rtc::Event event;
+  RecordingState old_state;
+  decode_queue_.PostTask([this, &event, &old_state, generate_key_frame,
+                          state = std::move(state)] {
+    RTC_DCHECK_RUN_ON(&decode_queue_);
+    // Save old state.
+    old_state.callback = std::move(encoded_frame_buffer_function_);
+    old_state.keyframe_needed = keyframe_generation_requested_;
+    old_state.last_keyframe_request_ms = last_keyframe_request_ms_;
+
+    // Set new state.
+    encoded_frame_buffer_function_ = std::move(state.callback);
+    if (generate_key_frame) {
+      RequestKeyFrame(clock_->TimeInMilliseconds());
+      keyframe_generation_requested_ = true;
+    } else {
+      keyframe_generation_requested_ = state.keyframe_needed;
+      last_keyframe_request_ms_ = state.last_keyframe_request_ms.value_or(0);
+    }
+    event.Set();
+  });
+  event.Wait(rtc::Event::kForever);
+  return old_state;
+}
+
+void VideoReceiveStream::GenerateKeyFrame() {
+  decode_queue_.PostTask([this]() {
+    RTC_DCHECK_RUN_ON(&decode_queue_);
+    RequestKeyFrame(clock_->TimeInMilliseconds());
+    keyframe_generation_requested_ = true;
+  });
+}
+
 }  // namespace internal
 }  // namespace webrtc
diff --git a/video/video_receive_stream.h b/video/video_receive_stream.h
index 2a4e0d1..f097710 100644
--- a/video/video_receive_stream.h
+++ b/video/video_receive_stream.h
@@ -15,6 +15,8 @@
 #include <vector>
 
 #include "api/task_queue/task_queue_factory.h"
+#include "api/transport/media/media_transport_interface.h"
+#include "api/video/recordable_encoded_frame.h"
 #include "call/rtp_packet_sink_interface.h"
 #include "call/syncable.h"
 #include "call/video_receive_stream.h"
@@ -50,6 +52,10 @@
                            public Syncable,
                            public CallStatsObserver {
  public:
+  // The default number of milliseconds to pass before re-requesting a key frame
+  // to be sent.
+  static constexpr int kMaxWaitForKeyFrameMs = 200;
+
   VideoReceiveStream(TaskQueueFactory* task_queue_factory,
                      RtpStreamReceiverControllerInterface* receiver_controller,
                      int num_cpu_cores,
@@ -123,15 +129,23 @@
 
   std::vector<webrtc::RtpSource> GetSources() const override;
 
+  RecordingState SetAndGetRecordingState(RecordingState state,
+                                         bool generate_key_frame) override;
+  void GenerateKeyFrame() override;
+
  private:
   int64_t GetWaitMs() const;
   void StartNextDecode() RTC_RUN_ON(decode_queue_);
-  void HandleEncodedFrame(std::unique_ptr<video_coding::EncodedFrame> frame);
-  void HandleFrameBufferTimeout();
-
+  void HandleEncodedFrame(std::unique_ptr<video_coding::EncodedFrame> frame)
+      RTC_RUN_ON(decode_queue_);
+  void HandleFrameBufferTimeout() RTC_RUN_ON(decode_queue_);
   void UpdatePlayoutDelays() const
       RTC_EXCLUSIVE_LOCKS_REQUIRED(playout_delay_lock_);
-  void RequestKeyFrame(int64_t timestamp_ms);
+  void RequestKeyFrame(int64_t timestamp_ms) RTC_RUN_ON(decode_queue_);
+  void HandleKeyFrameGeneration(bool received_frame_is_keyframe, int64_t now_ms)
+      RTC_RUN_ON(decode_queue_);
+  bool IsReceivingKeyFrame(int64_t timestamp_ms) const
+      RTC_RUN_ON(decode_queue_);
 
   void UpdateHistograms();
 
@@ -207,6 +221,12 @@
   // Maximum delay as decided by the RTP playout delay extension.
   int frame_maximum_playout_delay_ms_ RTC_GUARDED_BY(playout_delay_lock_) = -1;
 
+  // Function that is triggered with encoded frames, if not empty.
+  std::function<void(const RecordableEncodedFrame&)>
+      encoded_frame_buffer_function_ RTC_GUARDED_BY(decode_queue_);
+  // Set to true while we're requesting keyframes but not yet received one.
+  bool keyframe_generation_requested_ RTC_GUARDED_BY(decode_queue_) = false;
+
   // Defined last so they are destroyed before all other members.
   rtc::TaskQueue decode_queue_;
 };
diff --git a/video/video_receive_stream_unittest.cc b/video/video_receive_stream_unittest.cc
index c9d0ad1..2da7f12 100644
--- a/video/video_receive_stream_unittest.cc
+++ b/video/video_receive_stream_unittest.cc
@@ -24,6 +24,7 @@
 #include "modules/pacing/packet_router.h"
 #include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
 #include "modules/utility/include/process_thread.h"
+#include "modules/video_coding/encoded_frame.h"
 #include "rtc_base/critical_section.h"
 #include "rtc_base/event.h"
 #include "system_wrappers/include/clock.h"
@@ -31,6 +32,7 @@
 #include "test/field_trial.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
+#include "test/time_controller/simulated_time_controller.h"
 #include "test/video_decoder_proxy_factory.h"
 #include "video/call_stats.h"
 
@@ -239,7 +241,6 @@
         call_stats_(Clock::GetRealTimeClock(), process_thread_.get()) {}
 
   void SetUp() {
-    constexpr int kDefaultNumCpuCores = 2;
     config_.rtp.remote_ssrc = 1111;
     config_.rtp.local_ssrc = 2222;
     config_.renderer = &fake_renderer_;
@@ -249,12 +250,18 @@
     fake_decoder.decoder_factory = &fake_decoder_factory_;
     config_.decoders.push_back(fake_decoder);
     clock_ = Clock::GetRealTimeClock();
-    timing_ = new VCMTiming(clock_);
+    ReCreateReceiveStream(VideoReceiveStream::RecordingState());
+  }
 
+  void ReCreateReceiveStream(VideoReceiveStream::RecordingState state) {
+    constexpr int kDefaultNumCpuCores = 2;
+    video_receive_stream_ = nullptr;
+    timing_ = new VCMTiming(clock_);
     video_receive_stream_.reset(new webrtc::internal::VideoReceiveStream(
         task_queue_factory_.get(), &rtp_stream_receiver_controller_,
         kDefaultNumCpuCores, &packet_router_, config_.Copy(),
         process_thread_.get(), &call_stats_, clock_, timing_));
+    video_receive_stream_->SetAndGetRecordingState(std::move(state), false);
   }
 
  protected:
@@ -391,4 +398,160 @@
   }
 }
 
+std::unique_ptr<FrameObjectFake> MakeFrame(VideoFrameType frame_type,
+                                           int picture_id) {
+  auto frame = std::make_unique<FrameObjectFake>();
+  frame->SetPayloadType(99);
+  frame->id.picture_id = picture_id;
+  frame->SetFrameType(frame_type);
+  return frame;
+}
+
+TEST_F(VideoReceiveStreamTestWithFakeDecoder,
+       PassesFrameWhenEncodedFramesCallbackSet) {
+  testing::MockFunction<void(const RecordableEncodedFrame&)> callback;
+  video_receive_stream_->Start();
+  // Expect a keyframe request to be generated
+  EXPECT_CALL(mock_transport_, SendRtcp);
+  EXPECT_CALL(callback, Call);
+  video_receive_stream_->SetAndGetRecordingState(
+      VideoReceiveStream::RecordingState(callback.AsStdFunction()), true);
+  video_receive_stream_->OnCompleteFrame(
+      MakeFrame(VideoFrameType::kVideoFrameKey, 0));
+  EXPECT_TRUE(fake_renderer_.WaitForRenderedFrame(kDefaultTimeOutMs));
+  video_receive_stream_->Stop();
+}
+
+TEST_F(VideoReceiveStreamTestWithFakeDecoder,
+       MovesEncodedFrameDispatchStateWhenReCreating) {
+  testing::MockFunction<void(const RecordableEncodedFrame&)> callback;
+  video_receive_stream_->Start();
+  // Expect a key frame request over RTCP.
+  EXPECT_CALL(mock_transport_, SendRtcp).Times(1);
+  video_receive_stream_->SetAndGetRecordingState(
+      VideoReceiveStream::RecordingState(callback.AsStdFunction()), true);
+  video_receive_stream_->Stop();
+  VideoReceiveStream::RecordingState old_state =
+      video_receive_stream_->SetAndGetRecordingState(
+          VideoReceiveStream::RecordingState(), false);
+  ReCreateReceiveStream(std::move(old_state));
+  video_receive_stream_->Stop();
+}
+
+class VideoReceiveStreamTestWithSimulatedClock : public ::testing::Test {
+ public:
+  class FakeDecoder2 : public test::FakeDecoder {
+   public:
+    explicit FakeDecoder2(std::function<void()> decode_callback)
+        : callback_(decode_callback) {}
+
+    int32_t Decode(const EncodedImage& input,
+                   bool missing_frames,
+                   int64_t render_time_ms) override {
+      int32_t result =
+          FakeDecoder::Decode(input, missing_frames, render_time_ms);
+      callback_();
+      return result;
+    }
+
+   private:
+    std::function<void()> callback_;
+  };
+
+  static VideoReceiveStream::Config GetConfig(
+      Transport* transport,
+      VideoDecoderFactory* decoder_factory,
+      rtc::VideoSinkInterface<webrtc::VideoFrame>* renderer) {
+    VideoReceiveStream::Config config(transport);
+    config.rtp.remote_ssrc = 1111;
+    config.rtp.local_ssrc = 2222;
+    config.renderer = renderer;
+    VideoReceiveStream::Decoder fake_decoder;
+    fake_decoder.payload_type = 99;
+    fake_decoder.video_format = SdpVideoFormat("VP8");
+    fake_decoder.decoder_factory = decoder_factory;
+    config.decoders.push_back(fake_decoder);
+    return config;
+  }
+
+  VideoReceiveStreamTestWithSimulatedClock()
+      : time_controller_(Timestamp::ms(4711)),
+        fake_decoder_factory_([this] {
+          return std::make_unique<FakeDecoder2>([this] { OnFrameDecoded(); });
+        }),
+        process_thread_(time_controller_.CreateProcessThread("ProcessThread")),
+        config_(GetConfig(&mock_transport_,
+                          &fake_decoder_factory_,
+                          &fake_renderer_)),
+        call_stats_(time_controller_.GetClock(), process_thread_.get()),
+        video_receive_stream_(time_controller_.GetTaskQueueFactory(),
+                              &rtp_stream_receiver_controller_,
+                              /*num_cores=*/2,
+                              &packet_router_,
+                              config_.Copy(),
+                              process_thread_.get(),
+                              &call_stats_,
+                              time_controller_.GetClock(),
+                              new VCMTiming(time_controller_.GetClock())) {
+    time_controller_.InvokeWithControlledYield(
+        [this] { video_receive_stream_.Start(); });
+  }
+
+  ~VideoReceiveStreamTestWithSimulatedClock() {
+    time_controller_.InvokeWithControlledYield(
+        [this] { video_receive_stream_.Stop(); });
+  }
+
+  void OnFrameDecoded() { event_->Set(); }
+
+  void PassEncodedFrameAndWait(
+      std::unique_ptr<video_coding::EncodedFrame> frame) {
+    time_controller_.InvokeWithControlledYield([this, &frame] {
+      event_ = std::make_unique<rtc::Event>();
+      // This call will eventually end up in the Decoded method where the
+      // event is set.
+      video_receive_stream_.OnCompleteFrame(std::move(frame));
+      event_->Wait(rtc::Event::kForever);
+    });
+  }
+
+ protected:
+  GlobalSimulatedTimeController time_controller_;
+  test::FunctionVideoDecoderFactory fake_decoder_factory_;
+  std::unique_ptr<ProcessThread> process_thread_;
+  MockTransport mock_transport_;
+  cricket::FakeVideoRenderer fake_renderer_;
+  VideoReceiveStream::Config config_;
+  CallStats call_stats_;
+  PacketRouter packet_router_;
+  RtpStreamReceiverController rtp_stream_receiver_controller_;
+  webrtc::internal::VideoReceiveStream video_receive_stream_;
+  std::unique_ptr<rtc::Event> event_;
+};
+
+TEST_F(VideoReceiveStreamTestWithSimulatedClock,
+       RequestsKeyFramesUntilKeyFrameReceived) {
+  auto tick =
+      TimeDelta::ms(internal::VideoReceiveStream::kMaxWaitForKeyFrameMs / 2);
+  EXPECT_CALL(mock_transport_, SendRtcp).Times(1);
+  video_receive_stream_.GenerateKeyFrame();
+  PassEncodedFrameAndWait(MakeFrame(VideoFrameType::kVideoFrameDelta, 0));
+  time_controller_.Sleep(tick);
+  PassEncodedFrameAndWait(MakeFrame(VideoFrameType::kVideoFrameDelta, 1));
+  testing::Mock::VerifyAndClearExpectations(&mock_transport_);
+
+  // T+200ms: still no key frame received, expect key frame request sent again.
+  EXPECT_CALL(mock_transport_, SendRtcp).Times(1);
+  time_controller_.Sleep(tick);
+  PassEncodedFrameAndWait(MakeFrame(VideoFrameType::kVideoFrameDelta, 2));
+  testing::Mock::VerifyAndClearExpectations(&mock_transport_);
+
+  // T+200ms: now send a key frame - we should not observe new key frame
+  // requests after this.
+  EXPECT_CALL(mock_transport_, SendRtcp).Times(0);
+  PassEncodedFrameAndWait(MakeFrame(VideoFrameType::kVideoFrameKey, 3));
+  time_controller_.Sleep(2 * tick);
+  PassEncodedFrameAndWait(MakeFrame(VideoFrameType::kVideoFrameDelta, 4));
+}
+
 }  // namespace webrtc