| /* | 
 |  *  Copyright (c) 2022 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 "video/video_stream_buffer_controller.h" | 
 |  | 
 | #include <stdint.h> | 
 |  | 
 | #include <limits> | 
 | #include <memory> | 
 | #include <string> | 
 | #include <tuple> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | #include "absl/types/optional.h" | 
 | #include "absl/types/variant.h" | 
 | #include "api/metronome/test/fake_metronome.h" | 
 | #include "api/units/frequency.h" | 
 | #include "api/units/time_delta.h" | 
 | #include "api/units/timestamp.h" | 
 | #include "api/video/video_content_type.h" | 
 | #include "api/video/video_timing.h" | 
 | #include "rtc_base/checks.h" | 
 | #include "test/fake_encoded_frame.h" | 
 | #include "test/gmock.h" | 
 | #include "test/gtest.h" | 
 | #include "test/scoped_key_value_config.h" | 
 | #include "test/time_controller/simulated_time_controller.h" | 
 | #include "video/decode_synchronizer.h" | 
 | #include "video/task_queue_frame_decode_scheduler.h" | 
 |  | 
 | using ::testing::_; | 
 | using ::testing::AllOf; | 
 | using ::testing::Contains; | 
 | using ::testing::Each; | 
 | using ::testing::Eq; | 
 | using ::testing::IsEmpty; | 
 | using ::testing::Matches; | 
 | using ::testing::Ne; | 
 | using ::testing::Not; | 
 | using ::testing::Optional; | 
 | using ::testing::Pointee; | 
 | using ::testing::SizeIs; | 
 | using ::testing::VariantWith; | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | namespace { | 
 |  | 
 | constexpr size_t kFrameSize = 10; | 
 | constexpr uint32_t kFps30Rtp = 90000 / 30; | 
 | constexpr TimeDelta kFps30Delay = 1 / Frequency::Hertz(30); | 
 | constexpr Timestamp kClockStart = Timestamp::Millis(1000); | 
 |  | 
 | auto TimedOut() { | 
 |   return Optional(VariantWith<TimeDelta>(_)); | 
 | } | 
 |  | 
 | auto Frame(testing::Matcher<EncodedFrame> m) { | 
 |   return Optional(VariantWith<std::unique_ptr<EncodedFrame>>(Pointee(m))); | 
 | } | 
 |  | 
 | std::unique_ptr<test::FakeEncodedFrame> WithReceiveTimeFromRtpTimestamp( | 
 |     std::unique_ptr<test::FakeEncodedFrame> frame) { | 
 |   if (frame->RtpTimestamp() == 0) { | 
 |     frame->SetReceivedTime(kClockStart.ms()); | 
 |   } else { | 
 |     frame->SetReceivedTime( | 
 |         TimeDelta::Seconds(frame->RtpTimestamp() / 90000.0).ms() + | 
 |         kClockStart.ms()); | 
 |   } | 
 |   return frame; | 
 | } | 
 |  | 
 | class VCMTimingTest : public VCMTiming { | 
 |  public: | 
 |   using VCMTiming::VCMTiming; | 
 |   void IncomingTimestamp(uint32_t rtp_timestamp, | 
 |                          Timestamp last_packet_time) override { | 
 |     IncomingTimestampMocked(rtp_timestamp, last_packet_time); | 
 |     VCMTiming::IncomingTimestamp(rtp_timestamp, last_packet_time); | 
 |   } | 
 |  | 
 |   MOCK_METHOD(void, | 
 |               IncomingTimestampMocked, | 
 |               (uint32_t rtp_timestamp, Timestamp last_packet_time), | 
 |               ()); | 
 | }; | 
 |  | 
 | class VideoStreamBufferControllerStatsObserverMock | 
 |     : public VideoStreamBufferControllerStatsObserver { | 
 |  public: | 
 |   MOCK_METHOD(void, | 
 |               OnCompleteFrame, | 
 |               (bool is_keyframe, | 
 |                size_t size_bytes, | 
 |                VideoContentType content_type), | 
 |               (override)); | 
 |   MOCK_METHOD(void, OnDroppedFrames, (uint32_t num_dropped), (override)); | 
 |   MOCK_METHOD(void, | 
 |               OnDecodableFrame, | 
 |               (TimeDelta jitter_buffer_delay, | 
 |                TimeDelta target_delay, | 
 |                TimeDelta minimum_delay), | 
 |               (override)); | 
 |   MOCK_METHOD(void, | 
 |               OnFrameBufferTimingsUpdated, | 
 |               (int estimated_max_decode_time_ms, | 
 |                int current_delay_ms, | 
 |                int target_delay_ms, | 
 |                int jitter_delay_ms, | 
 |                int min_playout_delay_ms, | 
 |                int render_delay_ms), | 
 |               (override)); | 
 |   MOCK_METHOD(void, | 
 |               OnTimingFrameInfoUpdated, | 
 |               (const TimingFrameInfo& info), | 
 |               (override)); | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | constexpr auto kMaxWaitForKeyframe = TimeDelta::Millis(500); | 
 | constexpr auto kMaxWaitForFrame = TimeDelta::Millis(1500); | 
 | class VideoStreamBufferControllerFixture | 
 |     : public ::testing::WithParamInterface<std::tuple<bool, std::string>>, | 
 |       public FrameSchedulingReceiver { | 
 |  public: | 
 |   VideoStreamBufferControllerFixture() | 
 |       : sync_decoding_(std::get<0>(GetParam())), | 
 |         field_trials_(std::get<1>(GetParam())), | 
 |         time_controller_(kClockStart), | 
 |         clock_(time_controller_.GetClock()), | 
 |         fake_metronome_(TimeDelta::Millis(16)), | 
 |         decode_sync_(clock_, | 
 |                      &fake_metronome_, | 
 |                      time_controller_.GetMainThread()), | 
 |         timing_(clock_, field_trials_), | 
 |         buffer_(std::make_unique<VideoStreamBufferController>( | 
 |             clock_, | 
 |             time_controller_.GetMainThread(), | 
 |             &timing_, | 
 |             &stats_callback_, | 
 |             this, | 
 |             kMaxWaitForKeyframe, | 
 |             kMaxWaitForFrame, | 
 |             sync_decoding_ ? decode_sync_.CreateSynchronizedFrameScheduler() | 
 |                            : std::make_unique<TaskQueueFrameDecodeScheduler>( | 
 |                                  clock_, | 
 |                                  time_controller_.GetMainThread()), | 
 |             field_trials_)) { | 
 |     // Avoid starting with negative render times. | 
 |     timing_.set_min_playout_delay(TimeDelta::Millis(10)); | 
 |  | 
 |     ON_CALL(stats_callback_, OnDroppedFrames) | 
 |         .WillByDefault( | 
 |             [this](auto num_dropped) { dropped_frames_ += num_dropped; }); | 
 |   } | 
 |  | 
 |   ~VideoStreamBufferControllerFixture() override { | 
 |     if (buffer_) { | 
 |       buffer_->Stop(); | 
 |     } | 
 |     time_controller_.AdvanceTime(TimeDelta::Zero()); | 
 |   } | 
 |  | 
 |   void OnEncodedFrame(std::unique_ptr<EncodedFrame> frame) override { | 
 |     RTC_DCHECK(frame); | 
 |     SetWaitResult(std::move(frame)); | 
 |   } | 
 |  | 
 |   void OnDecodableFrameTimeout(TimeDelta wait_time) override { | 
 |     SetWaitResult(wait_time); | 
 |   } | 
 |  | 
 |   using WaitResult = | 
 |       absl::variant<std::unique_ptr<EncodedFrame>, TimeDelta /*wait_time*/>; | 
 |  | 
 |   absl::optional<WaitResult> WaitForFrameOrTimeout(TimeDelta wait) { | 
 |     if (wait_result_) { | 
 |       return std::move(wait_result_); | 
 |     } | 
 |     time_controller_.AdvanceTime(TimeDelta::Zero()); | 
 |     if (wait_result_) { | 
 |       return std::move(wait_result_); | 
 |     } | 
 |  | 
 |     Timestamp now = clock_->CurrentTime(); | 
 |     // TODO(bugs.webrtc.org/13756): Remove this when rtc::Thread uses uses | 
 |     // Timestamp instead of an integer milliseconds. This extra wait is needed | 
 |     // for some tests that use the metronome. This is due to rounding | 
 |     // milliseconds, affecting the precision of simulated time controller uses | 
 |     // when posting tasks from threads. | 
 |     TimeDelta potential_extra_wait = | 
 |         Timestamp::Millis((now + wait).ms()) - (now + wait); | 
 |  | 
 |     time_controller_.AdvanceTime(wait); | 
 |     if (potential_extra_wait > TimeDelta::Zero()) { | 
 |       time_controller_.AdvanceTime(potential_extra_wait); | 
 |     } | 
 |     return std::move(wait_result_); | 
 |   } | 
 |  | 
 |   void StartNextDecode() { | 
 |     ResetLastResult(); | 
 |     buffer_->StartNextDecode(false); | 
 |   } | 
 |  | 
 |   void StartNextDecodeForceKeyframe() { | 
 |     ResetLastResult(); | 
 |     buffer_->StartNextDecode(true); | 
 |   } | 
 |  | 
 |   void ResetLastResult() { wait_result_.reset(); } | 
 |  | 
 |   int dropped_frames() const { return dropped_frames_; } | 
 |  | 
 |  protected: | 
 |   const bool sync_decoding_; | 
 |   test::ScopedKeyValueConfig field_trials_; | 
 |   GlobalSimulatedTimeController time_controller_; | 
 |   Clock* const clock_; | 
 |   test::FakeMetronome fake_metronome_; | 
 |   DecodeSynchronizer decode_sync_; | 
 |  | 
 |   ::testing::NiceMock<VCMTimingTest> timing_; | 
 |   ::testing::NiceMock<VideoStreamBufferControllerStatsObserverMock> | 
 |       stats_callback_; | 
 |   std::unique_ptr<VideoStreamBufferController> buffer_; | 
 |  | 
 |  private: | 
 |   void SetWaitResult(WaitResult result) { | 
 |     RTC_DCHECK(!wait_result_); | 
 |     if (absl::holds_alternative<std::unique_ptr<EncodedFrame>>(result)) { | 
 |       RTC_DCHECK(absl::get<std::unique_ptr<EncodedFrame>>(result)); | 
 |     } | 
 |     wait_result_.emplace(std::move(result)); | 
 |   } | 
 |  | 
 |   uint32_t dropped_frames_ = 0; | 
 |   absl::optional<WaitResult> wait_result_; | 
 | }; | 
 |  | 
 | class VideoStreamBufferControllerTest | 
 |     : public ::testing::Test, | 
 |       public VideoStreamBufferControllerFixture {}; | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, | 
 |        InitialTimeoutAfterKeyframeTimeoutPeriod) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |   // No frame inserted. Timeout expected. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForKeyframe), TimedOut()); | 
 |  | 
 |   // No new timeout set since receiver has not started new decode. | 
 |   ResetLastResult(); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForKeyframe), Eq(absl::nullopt)); | 
 |  | 
 |   // Now that receiver has asked for new frame, a new timeout can occur. | 
 |   StartNextDecodeForceKeyframe(); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForKeyframe), TimedOut()); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, KeyFramesAreScheduled) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |   time_controller_.AdvanceTime(TimeDelta::Millis(50)); | 
 |  | 
 |   auto frame = test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build(); | 
 |   buffer_->InsertFrame(std::move(frame)); | 
 |  | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, | 
 |        DeltaFrameTimeoutAfterKeyframeExtracted) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |  | 
 |   time_controller_.AdvanceTime(TimeDelta::Millis(50)); | 
 |   auto frame = test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build(); | 
 |   buffer_->InsertFrame(std::move(frame)); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForKeyframe), | 
 |               Frame(test::WithId(0))); | 
 |  | 
 |   StartNextDecode(); | 
 |   time_controller_.AdvanceTime(TimeDelta::Millis(50)); | 
 |  | 
 |   // Timeouts should now happen at the normal frequency. | 
 |   const int expected_timeouts = 5; | 
 |   for (int i = 0; i < expected_timeouts; ++i) { | 
 |     EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForFrame), TimedOut()); | 
 |     StartNextDecode(); | 
 |   } | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, DependantFramesAreScheduled) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build()); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   StartNextDecode(); | 
 |  | 
 |   time_controller_.AdvanceTime(kFps30Delay); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(1) | 
 |                            .Time(kFps30Rtp) | 
 |                            .AsLast() | 
 |                            .Refs({0}) | 
 |                            .Build()); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(1))); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, SpatialLayersAreScheduled) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(0).SpatialLayer(0).Time(0).Build())); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(1).SpatialLayer(1).Time(0).Build())); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(2).SpatialLayer(2).Time(0).AsLast().Build())); | 
 |   EXPECT_THAT( | 
 |       WaitForFrameOrTimeout(TimeDelta::Zero()), | 
 |       Frame(AllOf(test::WithId(0), test::FrameWithSize(3 * kFrameSize)))); | 
 |  | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(3).Time(kFps30Rtp).SpatialLayer(0).Build())); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(4).Time(kFps30Rtp).SpatialLayer(1).Build())); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp(test::FakeFrameBuilder() | 
 |                                                            .Id(5) | 
 |                                                            .Time(kFps30Rtp) | 
 |                                                            .SpatialLayer(2) | 
 |                                                            .AsLast() | 
 |                                                            .Build())); | 
 |  | 
 |   StartNextDecode(); | 
 |   EXPECT_THAT( | 
 |       WaitForFrameOrTimeout(kFps30Delay * 10), | 
 |       Frame(AllOf(test::WithId(3), test::FrameWithSize(3 * kFrameSize)))); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, | 
 |        OutstandingFrameTasksAreCancelledAfterDeletion) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build())); | 
 |   // Get keyframe. Delta frame should now be scheduled. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   StartNextDecode(); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp(test::FakeFrameBuilder() | 
 |                                                            .Id(1) | 
 |                                                            .Time(kFps30Rtp) | 
 |                                                            .AsLast() | 
 |                                                            .Refs({0}) | 
 |                                                            .Build())); | 
 |   buffer_->Stop(); | 
 |   // Wait for 2x max wait time. Since we stopped, this should cause no timeouts | 
 |   // or frame-ready callbacks. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForFrame * 2), Eq(absl::nullopt)); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, FramesWaitForDecoderToComplete) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |  | 
 |   // Start with a keyframe. | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build()); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   ResetLastResult(); | 
 |   // Insert a delta frame. | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(1) | 
 |                            .Time(kFps30Rtp) | 
 |                            .AsLast() | 
 |                            .Refs({0}) | 
 |                            .Build()); | 
 |  | 
 |   // Advancing time should not result in a frame since the scheduler has not | 
 |   // been signalled that we are ready. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Eq(absl::nullopt)); | 
 |   // Signal ready. | 
 |   StartNextDecode(); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(1))); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, LateFrameDropped) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |   //   F1 | 
 |   //   / | 
 |   // F0 --> F2 | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build()); | 
 |   // Start with a keyframe. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   StartNextDecode(); | 
 |  | 
 |   // Simulate late F1 which arrives after F2. | 
 |   time_controller_.AdvanceTime(kFps30Delay * 2); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(2) | 
 |                            .Time(2 * kFps30Rtp) | 
 |                            .AsLast() | 
 |                            .Refs({0}) | 
 |                            .Build()); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(2))); | 
 |  | 
 |   StartNextDecode(); | 
 |  | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(1) | 
 |                            .Time(1 * kFps30Rtp) | 
 |                            .AsLast() | 
 |                            .Refs({0}) | 
 |                            .Build()); | 
 |   // Confirm frame 1 is never scheduled by timing out. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForFrame), TimedOut()); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, FramesFastForwardOnSystemHalt) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |   //   F1 | 
 |   //   / | 
 |   // F0 --> F2 | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build()); | 
 |  | 
 |   // Start with a keyframe. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   time_controller_.AdvanceTime(kFps30Delay); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(1) | 
 |                            .Time(kFps30Rtp) | 
 |                            .AsLast() | 
 |                            .Refs({0}) | 
 |                            .Build()); | 
 |   time_controller_.AdvanceTime(kFps30Delay); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(2) | 
 |                            .Time(2 * kFps30Rtp) | 
 |                            .AsLast() | 
 |                            .Refs({0}) | 
 |                            .Build()); | 
 |  | 
 |   // Halting time should result in F1 being skipped. | 
 |   time_controller_.AdvanceTime(kFps30Delay * 2); | 
 |   StartNextDecode(); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(2))); | 
 |   EXPECT_EQ(dropped_frames(), 1); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, ForceKeyFrame) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |   // Initial keyframe. | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build()); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   StartNextDecodeForceKeyframe(); | 
 |  | 
 |   // F2 is the next keyframe, and should be extracted since a keyframe was | 
 |   // forced. | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(1) | 
 |                            .Time(kFps30Rtp) | 
 |                            .AsLast() | 
 |                            .Refs({0}) | 
 |                            .Build()); | 
 |   buffer_->InsertFrame( | 
 |       test::FakeFrameBuilder().Id(2).Time(kFps30Rtp * 2).AsLast().Build()); | 
 |  | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay * 3), Frame(test::WithId(2))); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, SlowDecoderDropsTemporalLayers) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |   // 2 temporal layers, at 15fps per layer to make 30fps total. | 
 |   // Decoder is slower than 30fps, so last_frame() will be skipped. | 
 |   //   F1 --> F3 --> F5 | 
 |   //   /      /     / | 
 |   // F0 --> F2 --> F4 | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build()); | 
 |   // Keyframe received. | 
 |   // Don't start next decode until slow delay. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   time_controller_.AdvanceTime(kFps30Delay); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(1) | 
 |                            .Time(1 * kFps30Rtp) | 
 |                            .Refs({0}) | 
 |                            .AsLast() | 
 |                            .Build()); | 
 |   time_controller_.AdvanceTime(kFps30Delay); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(2) | 
 |                            .Time(2 * kFps30Rtp) | 
 |                            .Refs({0}) | 
 |                            .AsLast() | 
 |                            .Build()); | 
 |  | 
 |   // Simulate decode taking 3x FPS rate. | 
 |   time_controller_.AdvanceTime(kFps30Delay * 1.5); | 
 |   StartNextDecode(); | 
 |   // F2 is the best frame since decoding was so slow that F1 is too old. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay * 2), Frame(test::WithId(2))); | 
 |   EXPECT_EQ(dropped_frames(), 1); | 
 |   time_controller_.AdvanceTime(kFps30Delay / 2); | 
 |  | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(3) | 
 |                            .Time(3 * kFps30Rtp) | 
 |                            .Refs({1, 2}) | 
 |                            .AsLast() | 
 |                            .Build()); | 
 |   time_controller_.AdvanceTime(kFps30Delay / 2); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(4) | 
 |                            .Time(4 * kFps30Rtp) | 
 |                            .Refs({2}) | 
 |                            .AsLast() | 
 |                            .Build()); | 
 |   time_controller_.AdvanceTime(kFps30Delay / 2); | 
 |  | 
 |   // F4 is the best frame since decoding was so slow that F1 is too old. | 
 |   time_controller_.AdvanceTime(kFps30Delay); | 
 |   StartNextDecode(); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(4))); | 
 |  | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(5) | 
 |                            .Time(5 * kFps30Rtp) | 
 |                            .Refs({3, 4}) | 
 |                            .AsLast() | 
 |                            .Build()); | 
 |   time_controller_.AdvanceTime(kFps30Delay / 2); | 
 |  | 
 |   // F5 is not decodable since F4 was decoded, so a timeout is expected. | 
 |   time_controller_.AdvanceTime(TimeDelta::Millis(10)); | 
 |   StartNextDecode(); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForFrame), TimedOut()); | 
 |   // TODO(bugs.webrtc.org/13343): This should be 2 dropped frames since frames 1 | 
 |   // and 3 were dropped. However, frame_buffer2 does not mark frame 3 as dropped | 
 |   // which is a bug. Uncomment below when that is fixed for frame_buffer2 is | 
 |   // deleted. | 
 |   // EXPECT_EQ(dropped_frames(), 2); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, | 
 |        NewFrameInsertedWhileWaitingToReleaseFrame) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |   // Initial keyframe. | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build())); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   time_controller_.AdvanceTime(kFps30Delay / 2); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp(test::FakeFrameBuilder() | 
 |                                                            .Id(1) | 
 |                                                            .Time(kFps30Rtp) | 
 |                                                            .Refs({0}) | 
 |                                                            .AsLast() | 
 |                                                            .Build())); | 
 |   StartNextDecode(); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Eq(absl::nullopt)); | 
 |  | 
 |   // Scheduler is waiting to deliver Frame 1 now. Insert Frame 2. Frame 1 should | 
 |   // be delivered still. | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp(test::FakeFrameBuilder() | 
 |                                                            .Id(2) | 
 |                                                            .Time(kFps30Rtp * 2) | 
 |                                                            .Refs({0}) | 
 |                                                            .AsLast() | 
 |                                                            .Build())); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(1))); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, SameFrameNotScheduledTwice) { | 
 |   // A frame could be scheduled twice if last_frame() arrive out-of-order but | 
 |   // the older frame is old enough to be fast forwarded. | 
 |   // | 
 |   // 1. F2 arrives and is scheduled. | 
 |   // 2. F3 arrives, but scheduling will not change since F2 is next. | 
 |   // 3. F1 arrives late and scheduling is checked since it is before F2. F1 | 
 |   // fast-forwarded since it is older. | 
 |   // | 
 |   // F2 is the best frame, but should only be scheduled once, followed by F3. | 
 |   StartNextDecodeForceKeyframe(); | 
 |  | 
 |   // First keyframe. | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build())); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Millis(15)), | 
 |               Frame(test::WithId(0))); | 
 |  | 
 |   StartNextDecode(); | 
 |  | 
 |   // F2 arrives and is scheduled. | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(2).Time(2 * kFps30Rtp).AsLast().Build())); | 
 |  | 
 |   // F3 arrives before F2 is extracted. | 
 |   time_controller_.AdvanceTime(kFps30Delay); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(3).Time(3 * kFps30Rtp).AsLast().Build())); | 
 |  | 
 |   // F1 arrives and is fast-forwarded since it is too late. | 
 |   // F2 is already scheduled and should not be rescheduled. | 
 |   time_controller_.AdvanceTime(kFps30Delay / 2); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(1).Time(1 * kFps30Rtp).AsLast().Build())); | 
 |  | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(2))); | 
 |   StartNextDecode(); | 
 |  | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(3))); | 
 |   StartNextDecode(); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForFrame), TimedOut()); | 
 |   EXPECT_EQ(dropped_frames(), 1); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, TestStatsCallback) { | 
 |   EXPECT_CALL(stats_callback_, | 
 |               OnCompleteFrame(true, kFrameSize, VideoContentType::UNSPECIFIED)); | 
 |   EXPECT_CALL(stats_callback_, OnDecodableFrame); | 
 |   EXPECT_CALL(stats_callback_, OnFrameBufferTimingsUpdated); | 
 |  | 
 |   // Fake timing having received decoded frame. | 
 |   timing_.StopDecodeTimer(TimeDelta::Millis(1), clock_->CurrentTime()); | 
 |   StartNextDecodeForceKeyframe(); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build()); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   // Flush stats posted on the decode queue. | 
 |   time_controller_.AdvanceTime(TimeDelta::Zero()); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, | 
 |        FrameCompleteCalledOnceForDuplicateFrame) { | 
 |   EXPECT_CALL(stats_callback_, | 
 |               OnCompleteFrame(true, kFrameSize, VideoContentType::UNSPECIFIED)) | 
 |       .Times(1); | 
 |  | 
 |   StartNextDecodeForceKeyframe(); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build()); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder().Id(0).Time(0).AsLast().Build()); | 
 |   // Flush stats posted on the decode queue. | 
 |   time_controller_.AdvanceTime(TimeDelta::Zero()); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, | 
 |        FrameCompleteCalledOnceForSingleTemporalUnit) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |  | 
 |   // `OnCompleteFrame` should not be called for the first two frames since they | 
 |   // do not complete the temporal layer. | 
 |   EXPECT_CALL(stats_callback_, OnCompleteFrame(_, _, _)).Times(0); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder().Id(0).Time(0).Build()); | 
 |   buffer_->InsertFrame( | 
 |       test::FakeFrameBuilder().Id(1).Time(0).Refs({0}).Build()); | 
 |   time_controller_.AdvanceTime(TimeDelta::Zero()); | 
 |   // Flush stats posted on the decode queue. | 
 |   ::testing::Mock::VerifyAndClearExpectations(&stats_callback_); | 
 |  | 
 |   // Note that this frame is not marked as a keyframe since the last spatial | 
 |   // layer has dependencies. | 
 |   EXPECT_CALL(stats_callback_, | 
 |               OnCompleteFrame(false, kFrameSize, VideoContentType::UNSPECIFIED)) | 
 |       .Times(1); | 
 |   buffer_->InsertFrame( | 
 |       test::FakeFrameBuilder().Id(2).Time(0).Refs({0, 1}).AsLast().Build()); | 
 |   // Flush stats posted on the decode queue. | 
 |   time_controller_.AdvanceTime(TimeDelta::Zero()); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, | 
 |        FrameCompleteCalledOnceForCompleteTemporalUnit) { | 
 |   // FrameBuffer2 logs the complete frame on the arrival of the last layer. | 
 |   StartNextDecodeForceKeyframe(); | 
 |  | 
 |   // `OnCompleteFrame` should not be called for the first two frames since they | 
 |   // do not complete the temporal layer. Frame 1 arrives later, at which time | 
 |   // this frame can finally be considered complete. | 
 |   EXPECT_CALL(stats_callback_, OnCompleteFrame(_, _, _)).Times(0); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder().Id(0).Time(0).Build()); | 
 |   buffer_->InsertFrame( | 
 |       test::FakeFrameBuilder().Id(2).Time(0).Refs({0, 1}).AsLast().Build()); | 
 |   time_controller_.AdvanceTime(TimeDelta::Zero()); | 
 |   // Flush stats posted on the decode queue. | 
 |   ::testing::Mock::VerifyAndClearExpectations(&stats_callback_); | 
 |  | 
 |   EXPECT_CALL(stats_callback_, | 
 |               OnCompleteFrame(false, kFrameSize, VideoContentType::UNSPECIFIED)) | 
 |       .Times(1); | 
 |   buffer_->InsertFrame( | 
 |       test::FakeFrameBuilder().Id(1).Time(0).Refs({0}).Build()); | 
 |   // Flush stats posted on the decode queue. | 
 |   time_controller_.AdvanceTime(TimeDelta::Zero()); | 
 | } | 
 |  | 
 | // Note: This test takes a long time to run if the fake metronome is active. | 
 | // Since the test needs to wait for the timestamp to rollover, it has a fake | 
 | // delay of around 6.5 hours. Even though time is simulated, this will be | 
 | // around 1,500,000 metronome tick invocations. | 
 | TEST_P(VideoStreamBufferControllerTest, NextFrameWithOldTimestamp) { | 
 |   // Test inserting 31 frames and pause the stream for a long time before | 
 |   // frame 32. | 
 |   StartNextDecodeForceKeyframe(); | 
 |   constexpr uint32_t kBaseRtp = std::numeric_limits<uint32_t>::max() / 2; | 
 |  | 
 |   // First keyframe. The receive time must be explicitly set in this test since | 
 |   // the RTP derived time used in all tests does not work when the long pause | 
 |   // happens later in the test. | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(0) | 
 |                            .Time(kBaseRtp) | 
 |                            .ReceivedTime(clock_->CurrentTime()) | 
 |                            .AsLast() | 
 |                            .Build()); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(0))); | 
 |  | 
 |   // 1 more frame to warmup VCMTiming for 30fps. | 
 |   StartNextDecode(); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(1) | 
 |                            .Time(kBaseRtp + kFps30Rtp) | 
 |                            .ReceivedTime(clock_->CurrentTime()) | 
 |                            .AsLast() | 
 |                            .Build()); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(1))); | 
 |  | 
 |   // Pause the stream for such a long time it incurs an RTP timestamp rollover | 
 |   // by over half. | 
 |   constexpr uint32_t kLastRtp = kBaseRtp + kFps30Rtp; | 
 |   constexpr uint32_t kRolloverRtp = | 
 |       kLastRtp + std::numeric_limits<uint32_t>::max() / 2 + 1; | 
 |   constexpr Frequency kRtpHz = Frequency::KiloHertz(90); | 
 |   // Pause for corresponding delay such that RTP timestamp would increase this | 
 |   // much at 30fps. | 
 |   constexpr TimeDelta kRolloverDelay = | 
 |       (std::numeric_limits<uint32_t>::max() / 2 + 1) / kRtpHz; | 
 |  | 
 |   // Avoid timeout being set while waiting for the frame and before the receiver | 
 |   // is ready. | 
 |   ResetLastResult(); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForFrame), Eq(absl::nullopt)); | 
 |   time_controller_.AdvanceTime(kRolloverDelay - kMaxWaitForFrame); | 
 |   StartNextDecode(); | 
 |   buffer_->InsertFrame(test::FakeFrameBuilder() | 
 |                            .Id(2) | 
 |                            .Time(kRolloverRtp) | 
 |                            .ReceivedTime(clock_->CurrentTime()) | 
 |                            .AsLast() | 
 |                            .Build()); | 
 |   // FrameBuffer2 drops the frame, while FrameBuffer3 will continue the stream. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(2))); | 
 | } | 
 |  | 
 | TEST_P(VideoStreamBufferControllerTest, | 
 |        FrameNotSetForDecodedIfFrameBufferBecomesNonDecodable) { | 
 |   // This can happen if the frame buffer receives non-standard input. This test | 
 |   // will simply clear the frame buffer to replicate this. | 
 |   StartNextDecodeForceKeyframe(); | 
 |   // Initial keyframe. | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(0).Time(0).SpatialLayer(1).AsLast().Build())); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   // Insert a frame that will become non-decodable. | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp(test::FakeFrameBuilder() | 
 |                                                            .Id(11) | 
 |                                                            .Time(kFps30Rtp) | 
 |                                                            .Refs({0}) | 
 |                                                            .SpatialLayer(1) | 
 |                                                            .AsLast() | 
 |                                                            .Build())); | 
 |   StartNextDecode(); | 
 |   // Second layer inserted after last layer for the same frame out-of-order. | 
 |   // This second frame requires some older frame to be decoded and so now the | 
 |   // super-frame is no longer decodable despite already being scheduled. | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp(test::FakeFrameBuilder() | 
 |                                                            .Id(10) | 
 |                                                            .Time(kFps30Rtp) | 
 |                                                            .SpatialLayer(0) | 
 |                                                            .Refs({2}) | 
 |                                                            .Build())); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForFrame), TimedOut()); | 
 |  | 
 |   // Ensure that this frame can be decoded later. | 
 |   StartNextDecode(); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp(test::FakeFrameBuilder() | 
 |                                                            .Id(2) | 
 |                                                            .Time(kFps30Rtp / 2) | 
 |                                                            .SpatialLayer(0) | 
 |                                                            .Refs({0}) | 
 |                                                            .AsLast() | 
 |                                                            .Build())); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(2))); | 
 |   StartNextDecode(); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(10))); | 
 | } | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P(VideoStreamBufferController, | 
 |                          VideoStreamBufferControllerTest, | 
 |                          ::testing::Combine(::testing::Bool(), | 
 |                                             ::testing::Values("")), | 
 |                          [](const auto& info) { | 
 |                            return std::get<0>(info.param) ? "SyncDecoding" | 
 |                                                           : "UnsyncedDecoding"; | 
 |                          }); | 
 |  | 
 | class LowLatencyVideoStreamBufferControllerTest | 
 |     : public ::testing::Test, | 
 |       public VideoStreamBufferControllerFixture {}; | 
 |  | 
 | TEST_P(LowLatencyVideoStreamBufferControllerTest, | 
 |        FramesDecodedInstantlyWithLowLatencyRendering) { | 
 |   // Initial keyframe. | 
 |   StartNextDecodeForceKeyframe(); | 
 |   timing_.set_min_playout_delay(TimeDelta::Zero()); | 
 |   timing_.set_max_playout_delay(TimeDelta::Millis(10)); | 
 |   // Playout delay of 0 implies low-latency rendering. | 
 |   auto frame = test::FakeFrameBuilder() | 
 |                    .Id(0) | 
 |                    .Time(0) | 
 |                    .PlayoutDelay({TimeDelta::Zero(), TimeDelta::Millis(10)}) | 
 |                    .AsLast() | 
 |                    .Build(); | 
 |   buffer_->InsertFrame(std::move(frame)); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   // Delta frame would normally wait here, but should decode at the pacing rate | 
 |   // in low-latency mode. | 
 |   StartNextDecode(); | 
 |   frame = test::FakeFrameBuilder() | 
 |               .Id(1) | 
 |               .Time(kFps30Rtp) | 
 |               .PlayoutDelay({TimeDelta::Zero(), TimeDelta::Millis(10)}) | 
 |               .AsLast() | 
 |               .Build(); | 
 |   buffer_->InsertFrame(std::move(frame)); | 
 |   // Pacing is set to 16ms in the field trial so we should not decode yet. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Eq(absl::nullopt)); | 
 |   time_controller_.AdvanceTime(TimeDelta::Millis(16)); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(1))); | 
 | } | 
 |  | 
 | TEST_P(LowLatencyVideoStreamBufferControllerTest, ZeroPlayoutDelayFullQueue) { | 
 |   // Initial keyframe. | 
 |   StartNextDecodeForceKeyframe(); | 
 |   timing_.set_min_playout_delay(TimeDelta::Zero()); | 
 |   timing_.set_max_playout_delay(TimeDelta::Millis(10)); | 
 |   auto frame = test::FakeFrameBuilder() | 
 |                    .Id(0) | 
 |                    .Time(0) | 
 |                    .PlayoutDelay({TimeDelta::Zero(), TimeDelta::Millis(10)}) | 
 |                    .AsLast() | 
 |                    .Build(); | 
 |   // Playout delay of 0 implies low-latency rendering. | 
 |   buffer_->InsertFrame(std::move(frame)); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   // Queue up 5 frames (configured max queue size for 0-playout delay pacing). | 
 |   for (int id = 1; id <= 6; ++id) { | 
 |     frame = test::FakeFrameBuilder() | 
 |                 .Id(id) | 
 |                 .Time(kFps30Rtp * id) | 
 |                 .PlayoutDelay({TimeDelta::Zero(), TimeDelta::Millis(10)}) | 
 |                 .AsLast() | 
 |                 .Build(); | 
 |     buffer_->InsertFrame(std::move(frame)); | 
 |   } | 
 |  | 
 |   // The queue is at its max size for zero playout delay pacing, so the pacing | 
 |   // should be ignored and the next frame should be decoded instantly. | 
 |   StartNextDecode(); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(1))); | 
 | } | 
 |  | 
 | TEST_P(LowLatencyVideoStreamBufferControllerTest, | 
 |        MinMaxDelayZeroLowLatencyMode) { | 
 |   // Initial keyframe. | 
 |   StartNextDecodeForceKeyframe(); | 
 |   timing_.set_min_playout_delay(TimeDelta::Zero()); | 
 |   timing_.set_max_playout_delay(TimeDelta::Zero()); | 
 |   // Playout delay of 0 implies low-latency rendering. | 
 |   auto frame = test::FakeFrameBuilder() | 
 |                    .Id(0) | 
 |                    .Time(0) | 
 |                    .PlayoutDelay(VideoPlayoutDelay::Minimal()) | 
 |                    .AsLast() | 
 |                    .Build(); | 
 |   buffer_->InsertFrame(std::move(frame)); | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(0))); | 
 |  | 
 |   // Delta frame would normally wait here, but should decode at the pacing rate | 
 |   // in low-latency mode. | 
 |   StartNextDecode(); | 
 |   frame = test::FakeFrameBuilder() | 
 |               .Id(1) | 
 |               .Time(kFps30Rtp) | 
 |               .PlayoutDelay(VideoPlayoutDelay::Minimal()) | 
 |               .AsLast() | 
 |               .Build(); | 
 |   buffer_->InsertFrame(std::move(frame)); | 
 |   // The min/max=0 version of low-latency rendering will result in a large | 
 |   // negative decode wait time, so the frame should be ready right away. | 
 |   EXPECT_THAT(WaitForFrameOrTimeout(TimeDelta::Zero()), Frame(test::WithId(1))); | 
 | } | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P( | 
 |     VideoStreamBufferController, | 
 |     LowLatencyVideoStreamBufferControllerTest, | 
 |     ::testing::Combine( | 
 |         ::testing::Bool(), | 
 |         ::testing::Values( | 
 |             "WebRTC-ZeroPlayoutDelay/min_pacing:16ms,max_decode_queue_size:5/", | 
 |             "WebRTC-ZeroPlayoutDelay/" | 
 |             "min_pacing:16ms,max_decode_queue_size:5/"))); | 
 |  | 
 | class IncomingTimestampVideoStreamBufferControllerTest | 
 |     : public ::testing::Test, | 
 |       public VideoStreamBufferControllerFixture {}; | 
 |  | 
 | TEST_P(IncomingTimestampVideoStreamBufferControllerTest, | 
 |        IncomingTimestampOnMarkerBitOnly) { | 
 |   StartNextDecodeForceKeyframe(); | 
 |   EXPECT_CALL(timing_, IncomingTimestampMocked) | 
 |       .Times(field_trials_.IsDisabled("WebRTC-IncomingTimestampOnMarkerBitOnly") | 
 |                  ? 3 | 
 |                  : 1); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(0).SpatialLayer(0).Time(0).Build())); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(1).SpatialLayer(1).Time(0).Build())); | 
 |   buffer_->InsertFrame(WithReceiveTimeFromRtpTimestamp( | 
 |       test::FakeFrameBuilder().Id(2).SpatialLayer(2).Time(0).AsLast().Build())); | 
 | } | 
 |  | 
 | INSTANTIATE_TEST_SUITE_P( | 
 |     VideoStreamBufferController, | 
 |     IncomingTimestampVideoStreamBufferControllerTest, | 
 |     ::testing::Combine( | 
 |         ::testing::Bool(), | 
 |         ::testing::Values( | 
 |             "WebRTC-IncomingTimestampOnMarkerBitOnly/Enabled/", | 
 |             "WebRTC-IncomingTimestampOnMarkerBitOnly/Disabled/"))); | 
 |  | 
 | }  // namespace webrtc |