diff --git a/video/BUILD.gn b/video/BUILD.gn index 6e59619481..65f02d84e3 100644 --- a/video/BUILD.gn +++ b/video/BUILD.gn @@ -280,7 +280,6 @@ rtc_library("frame_buffer_proxy") { "../rtc_base:logging", "../rtc_base:macromagic", "../rtc_base:rtc_task_queue", - "../rtc_base/experiments:rtt_mult_experiment", "../system_wrappers", "../system_wrappers:field_trial", ] diff --git a/video/frame_buffer_proxy.cc b/video/frame_buffer_proxy.cc index 1d28d94056..f69d9da124 100644 --- a/video/frame_buffer_proxy.cc +++ b/video/frame_buffer_proxy.cc @@ -21,11 +21,11 @@ #include "api/video/encoded_frame.h" #include "api/video/frame_buffer.h" #include "api/video/video_content_type.h" +#include "modules/video_coding/frame_buffer2.h" #include "modules/video_coding/frame_helpers.h" #include "modules/video_coding/timing/inter_frame_delay.h" #include "modules/video_coding/timing/jitter_estimator.h" #include "rtc_base/checks.h" -#include "rtc_base/experiments/rtt_mult_experiment.h" #include "rtc_base/logging.h" #include "rtc_base/thread_annotations.h" #include "video/frame_decode_timing.h" @@ -36,6 +36,104 @@ namespace webrtc { namespace { +class FrameBuffer2Proxy : public FrameBufferProxy { + public: + FrameBuffer2Proxy(Clock* clock, + VCMTiming* timing, + VCMReceiveStatisticsCallback* stats_proxy, + rtc::TaskQueue* decode_queue, + FrameSchedulingReceiver* receiver, + TimeDelta max_wait_for_keyframe, + TimeDelta max_wait_for_frame, + const FieldTrialsView& field_trials) + : max_wait_for_keyframe_(max_wait_for_keyframe), + max_wait_for_frame_(max_wait_for_frame), + frame_buffer_(clock, timing, stats_proxy, field_trials), + decode_queue_(decode_queue), + stats_proxy_(stats_proxy), + receiver_(receiver) { + RTC_DCHECK(decode_queue_); + RTC_DCHECK(stats_proxy_); + RTC_DCHECK(receiver_); + } + + void StopOnWorker() override { + RTC_DCHECK_RUN_ON(&worker_sequence_checker_); + decode_queue_->PostTask([this] { + frame_buffer_.Stop(); + decode_safety_->SetNotAlive(); + }); + } + + void SetProtectionMode(VCMVideoProtection protection_mode) override { + RTC_DCHECK_RUN_ON(&worker_sequence_checker_); + frame_buffer_.SetProtectionMode(kProtectionNackFEC); + } + + void Clear() override { + RTC_DCHECK_RUN_ON(&worker_sequence_checker_); + frame_buffer_.Clear(); + } + + absl::optional InsertFrame( + std::unique_ptr frame) override { + RTC_DCHECK_RUN_ON(&worker_sequence_checker_); + int64_t last_continuous_pid = frame_buffer_.InsertFrame(std::move(frame)); + if (last_continuous_pid != -1) + return last_continuous_pid; + return absl::nullopt; + } + + void UpdateRtt(int64_t max_rtt_ms) override { + RTC_DCHECK_RUN_ON(&worker_sequence_checker_); + frame_buffer_.UpdateRtt(max_rtt_ms); + } + + void StartNextDecode(bool keyframe_required) override { + if (!decode_queue_->IsCurrent()) { + decode_queue_->PostTask(ToQueuedTask( + decode_safety_, + [this, keyframe_required] { StartNextDecode(keyframe_required); })); + return; + } + RTC_DCHECK_RUN_ON(decode_queue_); + + frame_buffer_.NextFrame( + MaxWait(keyframe_required).ms(), keyframe_required, decode_queue_, + /* encoded frame handler */ + [this, keyframe_required](std::unique_ptr frame) { + RTC_DCHECK_RUN_ON(decode_queue_); + if (!decode_safety_->alive()) + return; + if (frame) { + receiver_->OnEncodedFrame(std::move(frame)); + } else { + receiver_->OnDecodableFrameTimeout(MaxWait(keyframe_required)); + } + }); + } + + int Size() override { + RTC_DCHECK_RUN_ON(&worker_sequence_checker_); + return frame_buffer_.Size(); + } + + private: + TimeDelta MaxWait(bool keyframe_required) const { + return keyframe_required ? max_wait_for_keyframe_ : max_wait_for_frame_; + } + + RTC_NO_UNIQUE_ADDRESS SequenceChecker worker_sequence_checker_; + const TimeDelta max_wait_for_keyframe_; + const TimeDelta max_wait_for_frame_; + video_coding::FrameBuffer frame_buffer_; + rtc::TaskQueue* const decode_queue_; + VCMReceiveStatisticsCallback* const stats_proxy_; + FrameSchedulingReceiver* const receiver_; + rtc::scoped_refptr decode_safety_ = + PendingTaskSafetyFlag::CreateDetached(); +}; + // Max number of frames the buffer will hold. static constexpr size_t kMaxFramesBuffered = 800; // Max number of decoded frame info that will be saved. @@ -436,6 +534,7 @@ class FrameBuffer3Proxy : public FrameBufferProxy { }; enum class FrameBufferArm { + kFrameBuffer2, kFrameBuffer3, kSyncDecode, }; @@ -444,8 +543,9 @@ constexpr const char* kFrameBufferFieldTrial = "WebRTC-FrameBuffer3"; FrameBufferArm ParseFrameBufferFieldTrial(const FieldTrialsView& field_trials) { webrtc::FieldTrialEnum arm( - "arm", FrameBufferArm::kFrameBuffer3, + "arm", FrameBufferArm::kFrameBuffer2, { + {"FrameBuffer2", FrameBufferArm::kFrameBuffer2}, {"FrameBuffer3", FrameBufferArm::kFrameBuffer3}, {"SyncDecoding", FrameBufferArm::kSyncDecode}, }); @@ -467,6 +567,10 @@ std::unique_ptr FrameBufferProxy::CreateFromFieldTrial( DecodeSynchronizer* decode_sync, const FieldTrialsView& field_trials) { switch (ParseFrameBufferFieldTrial(field_trials)) { + case FrameBufferArm::kFrameBuffer2: + return std::make_unique( + clock, timing, stats_proxy, decode_queue, receiver, + max_wait_for_keyframe, max_wait_for_frame, field_trials); case FrameBufferArm::kSyncDecode: { std::unique_ptr scheduler; if (decode_sync) { diff --git a/video/frame_buffer_proxy_unittest.cc b/video/frame_buffer_proxy_unittest.cc index 4d4aae4520..6babcdada9 100644 --- a/video/frame_buffer_proxy_unittest.cc +++ b/video/frame_buffer_proxy_unittest.cc @@ -27,6 +27,7 @@ #include "api/video/video_content_type.h" #include "api/video/video_timing.h" #include "rtc_base/checks.h" +#include "rtc_base/event.h" #include "test/fake_encoded_frame.h" #include "test/gmock.h" #include "test/gtest.h" @@ -103,6 +104,11 @@ class VCMReceiveStatisticsCallbackMock : public VCMReceiveStatisticsCallback { (override)); }; +bool IsFrameBuffer2Enabled(const FieldTrialsView& field_trials) { + return field_trials.Lookup("WebRTC-FrameBuffer3").find("arm:FrameBuffer2") != + std::string::npos; +} + } // namespace constexpr auto kMaxWaitForKeyframe = TimeDelta::Millis(500); @@ -233,7 +239,7 @@ class FrameBufferProxyTest : public ::testing::Test, TEST_P(FrameBufferProxyTest, InitialTimeoutAfterKeyframeTimeoutPeriod) { StartNextDecodeForceKeyframe(); - // No frame inserted. Timeout expected. + // No frame insterted. Timeout expected. EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForKeyframe), TimedOut()); // No new timeout set since receiver has not started new decode. @@ -658,6 +664,8 @@ TEST_P(FrameBufferProxyTest, FrameCompleteCalledOnceForSingleTemporalUnit) { TEST_P(FrameBufferProxyTest, FrameCompleteCalledOnceForCompleteTemporalUnit) { // FrameBuffer2 logs the complete frame on the arrival of the last layer. + if (IsFrameBuffer2Enabled(field_trials_)) + return; StartNextDecodeForceKeyframe(); // `OnCompleteFrame` should not be called for the first two frames since they @@ -734,13 +742,18 @@ TEST_P(FrameBufferProxyTest, NextFrameWithOldTimestamp) { .AsLast() .Build()); // FrameBuffer2 drops the frame, while FrameBuffer3 will continue the stream. - EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(2))); + if (!IsFrameBuffer2Enabled(field_trials_)) { + EXPECT_THAT(WaitForFrameOrTimeout(kFps30Delay), Frame(test::WithId(2))); + } else { + EXPECT_THAT(WaitForFrameOrTimeout(kMaxWaitForFrame), TimedOut()); + } } INSTANTIATE_TEST_SUITE_P( FrameBufferProxy, FrameBufferProxyTest, - ::testing::Values("WebRTC-FrameBuffer3/arm:FrameBuffer3/", + ::testing::Values("WebRTC-FrameBuffer3/arm:FrameBuffer2/", + "WebRTC-FrameBuffer3/arm:FrameBuffer3/", "WebRTC-FrameBuffer3/arm:SyncDecoding/")); class LowLatencyFrameBufferProxyTest : public ::testing::Test, @@ -821,6 +834,8 @@ INSTANTIATE_TEST_SUITE_P( FrameBufferProxy, LowLatencyFrameBufferProxyTest, ::testing::Values( + "WebRTC-FrameBuffer3/arm:FrameBuffer2/" + "WebRTC-ZeroPlayoutDelay/min_pacing:16ms,max_decode_queue_size:5/", "WebRTC-FrameBuffer3/arm:FrameBuffer3/" "WebRTC-ZeroPlayoutDelay/min_pacing:16ms,max_decode_queue_size:5/", "WebRTC-FrameBuffer3/arm:SyncDecoding/"