Metronome: disable & refactor for single-threaded operation.

The Chromium implementation unfortunately has a rare deadlock.
Rather than patching that up, we're changing the metronome
implementation to be able to use a single-threaded environment
instead.

The metronome functionality is disabled in VideoReceiveStream2
construction inside call.cc.

The new design does not have listener registration or
deresigstration and instead accepts and invokes callbacks, on
the same sequence that requested the callback. This allows
the clients to use features such as WeakPtrFactories or
ScopedThreadSafety for cancellation.

The CL will be followed up with cleanup CLs that removes
registration APIs once downstream consumers have adapted.

Bug: chromium:1381982
Change-Id: I43732d1971e2276c39b431a04365cd2fc3c55c25
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/282280
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Evan Shrubsole <eshr@webrtc.org>
Commit-Queue: Markus Handell <handellm@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#38582}
This commit is contained in:
Markus Handell 2022-11-08 12:14:23 +01:00 committed by WebRTC LUCI CQ
parent 822794d491
commit be400e465b
16 changed files with 151 additions and 156 deletions

View File

@ -10,7 +10,10 @@ import("../../webrtc.gni")
rtc_source_set("metronome") { rtc_source_set("metronome") {
visibility = [ "*" ] visibility = [ "*" ]
sources = [ "metronome.h" ] sources = [
"metronome.cc",
"metronome.h",
]
deps = [ deps = [
"../../rtc_base/system:rtc_export", "../../rtc_base/system:rtc_export",
"../task_queue", "../task_queue",

View File

@ -0,0 +1,19 @@
/*
* 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 "api/metronome/metronome.h"
namespace webrtc {
// TODO(crbug.com/1381982): Remove outdated methods.
void Metronome::AddListener(TickListener* listener) {}
void Metronome::RemoveListener(TickListener* listener) {}
} // namespace webrtc

View File

@ -17,44 +17,40 @@
namespace webrtc { namespace webrtc {
// The Metronome posts OnTick() on task queues provided by its listeners' task // The Metronome posts OnTick() calls requested with RequestCallOnNextTick.
// queue periodically. The metronome can be used as an alternative to using // The API is designed to be fully used from a single task queue. Scheduled
// PostDelayedTask on a thread or task queue for coalescing work and reducing // callbacks are executed on the same sequence as they were requested on. There
// the number of idle-wakeups. // are no features implemented for cancellation. When that's needed, use e.g.
// // ScopedTaskSafety from the client.
// Listeners can be added and removed from any sequence, but it is illegal to
// remove a listener from an OnTick invocation.
// //
// The metronome concept is still under experimentation, and may not be availble // The metronome concept is still under experimentation, and may not be availble
// in all platforms or applications. See https://crbug.com/1253787 for more // in all platforms or applications. See https://crbug.com/1253787 for more
// details. // details.
// //
// Metronome implementations must be thread-safe. // Metronome implementations must be thread-compatible.
class RTC_EXPORT Metronome { class RTC_EXPORT Metronome {
public: public:
// TODO(crbug.com/1381982): remove stale classes and methods once downstream
// dependencies adapts.
class RTC_EXPORT TickListener { class RTC_EXPORT TickListener {
public: public:
virtual ~TickListener() = default; virtual ~TickListener() = default;
// OnTick is run on the task queue provided by OnTickTaskQueue each time the
// metronome ticks.
virtual void OnTick() = 0; virtual void OnTick() = 0;
// The task queue that OnTick will run on. Must not be null.
virtual TaskQueueBase* OnTickTaskQueue() = 0; virtual TaskQueueBase* OnTickTaskQueue() = 0;
}; };
virtual ~Metronome() = default; virtual ~Metronome() = default;
// Adds a tick listener to the metronome. Once this method has returned // TODO(crbug.com/1381982): remove stale classes and methods once downstream
// OnTick will be invoked on each metronome tick. A listener may // dependencies adapts.
// only be added to the metronome once. virtual void AddListener(TickListener* listener);
virtual void AddListener(TickListener* listener) = 0; virtual void RemoveListener(TickListener* listener);
// Removes the tick listener from the metronome. Once this method has returned // Requests a call to `callback` on the next tick. Scheduled callbacks are
// OnTick will never be called again. This method must not be called from // executed on the same sequence as they were requested on. There are no
// within OnTick. // features for cancellation. When that's needed, use e.g. ScopedTaskSafety
virtual void RemoveListener(TickListener* listener) = 0; // from the client.
virtual void RequestCallOnNextTick(absl::AnyInvocable<void() &&> callback) {}
// Returns the current tick period of the metronome. // Returns the current tick period of the metronome.
virtual TimeDelta TickPeriod() const = 0; virtual TimeDelta TickPeriod() const = 0;

View File

@ -23,6 +23,7 @@ rtc_library("fake_metronome") {
"../../../rtc_base:rtc_task_queue", "../../../rtc_base:rtc_task_queue",
"../../../rtc_base/synchronization:mutex", "../../../rtc_base/synchronization:mutex",
"../../../rtc_base/task_utils:repeating_task", "../../../rtc_base/task_utils:repeating_task",
"../../../test:test_support",
"../../task_queue", "../../task_queue",
"../../units:time_delta", "../../units:time_delta",
] ]

View File

@ -10,8 +10,12 @@
#include "api/metronome/test/fake_metronome.h" #include "api/metronome/test/fake_metronome.h"
#include <utility>
#include <vector>
#include "api/priority.h" #include "api/priority.h"
#include "api/sequence_checker.h" #include "api/sequence_checker.h"
#include "api/task_queue/task_queue_base.h"
#include "api/task_queue/task_queue_factory.h" #include "api/task_queue/task_queue_factory.h"
#include "api/units/time_delta.h" #include "api/units/time_delta.h"
#include "rtc_base/event.h" #include "rtc_base/event.h"
@ -22,12 +26,9 @@ namespace webrtc::test {
ForcedTickMetronome::ForcedTickMetronome(TimeDelta tick_period) ForcedTickMetronome::ForcedTickMetronome(TimeDelta tick_period)
: tick_period_(tick_period) {} : tick_period_(tick_period) {}
void ForcedTickMetronome::AddListener(TickListener* listener) { void ForcedTickMetronome::RequestCallOnNextTick(
listeners_.insert(listener); absl::AnyInvocable<void() &&> callback) {
} callbacks_.push_back(std::move(callback));
void ForcedTickMetronome::RemoveListener(TickListener* listener) {
listeners_.erase(listener);
} }
TimeDelta ForcedTickMetronome::TickPeriod() const { TimeDelta ForcedTickMetronome::TickPeriod() const {
@ -35,55 +36,35 @@ TimeDelta ForcedTickMetronome::TickPeriod() const {
} }
size_t ForcedTickMetronome::NumListeners() { size_t ForcedTickMetronome::NumListeners() {
return listeners_.size(); return callbacks_.size();
} }
void ForcedTickMetronome::Tick() { void ForcedTickMetronome::Tick() {
for (auto* listener : listeners_) { std::vector<absl::AnyInvocable<void() &&>> callbacks;
listener->OnTickTaskQueue()->PostTask([listener] { listener->OnTick(); }); callbacks_.swap(callbacks);
for (auto& callback : callbacks)
std::move(callback)();
}
FakeMetronome::FakeMetronome(TimeDelta tick_period)
: tick_period_(tick_period) {}
void FakeMetronome::RequestCallOnNextTick(
absl::AnyInvocable<void() &&> callback) {
TaskQueueBase* current = TaskQueueBase::Current();
callbacks_.push_back(std::move(callback));
if (callbacks_.size() == 1) {
current->PostDelayedTask(
[this] {
std::vector<absl::AnyInvocable<void() &&>> callbacks;
callbacks_.swap(callbacks);
for (auto& callback : callbacks)
std::move(callback)();
},
tick_period_);
} }
} }
FakeMetronome::FakeMetronome(TaskQueueFactory* factory, TimeDelta tick_period)
: tick_period_(tick_period),
queue_(factory->CreateTaskQueue("MetronomeQueue",
TaskQueueFactory::Priority::HIGH)) {}
FakeMetronome::~FakeMetronome() {
RTC_DCHECK(listeners_.empty());
}
void FakeMetronome::AddListener(TickListener* listener) {
MutexLock lock(&mutex_);
listeners_.insert(listener);
if (!started_) {
tick_task_ = RepeatingTaskHandle::Start(queue_.Get(), [this] {
MutexLock lock(&mutex_);
// Stop if empty.
if (listeners_.empty())
return TimeDelta::PlusInfinity();
for (auto* listener : listeners_) {
listener->OnTickTaskQueue()->PostTask(
[listener] { listener->OnTick(); });
}
return tick_period_;
});
started_ = true;
}
}
void FakeMetronome::RemoveListener(TickListener* listener) {
MutexLock lock(&mutex_);
listeners_.erase(listener);
}
void FakeMetronome::Stop() {
MutexLock lock(&mutex_);
RTC_DCHECK(listeners_.empty());
if (started_)
queue_.PostTask([this] { tick_task_.Stop(); });
}
TimeDelta FakeMetronome::TickPeriod() const { TimeDelta FakeMetronome::TickPeriod() const {
return tick_period_; return tick_period_;
} }

View File

@ -13,6 +13,7 @@
#include <memory> #include <memory>
#include <set> #include <set>
#include <vector>
#include "api/metronome/metronome.h" #include "api/metronome/metronome.h"
#include "api/task_queue/task_queue_base.h" #include "api/task_queue/task_queue_base.h"
@ -36,13 +37,12 @@ class ForcedTickMetronome : public Metronome {
size_t NumListeners(); size_t NumListeners();
// Metronome implementation. // Metronome implementation.
void AddListener(TickListener* listener) override; void RequestCallOnNextTick(absl::AnyInvocable<void() &&> callback) override;
void RemoveListener(TickListener* listener) override;
TimeDelta TickPeriod() const override; TimeDelta TickPeriod() const override;
private: private:
const TimeDelta tick_period_; const TimeDelta tick_period_;
std::set<TickListener*> listeners_; std::vector<absl::AnyInvocable<void() &&>> callbacks_;
}; };
// FakeMetronome is a metronome that ticks based on a repeating task at the // FakeMetronome is a metronome that ticks based on a repeating task at the
@ -53,23 +53,15 @@ class ForcedTickMetronome : public Metronome {
// on the proper task queue. // on the proper task queue.
class FakeMetronome : public Metronome { class FakeMetronome : public Metronome {
public: public:
FakeMetronome(TaskQueueFactory* factory, TimeDelta tick_period); explicit FakeMetronome(TimeDelta tick_period);
~FakeMetronome() override;
// Metronome implementation. // Metronome implementation.
void AddListener(TickListener* listener) override; void RequestCallOnNextTick(absl::AnyInvocable<void() &&> callback) override;
void RemoveListener(TickListener* listener) override;
TimeDelta TickPeriod() const override; TimeDelta TickPeriod() const override;
void Stop();
private: private:
const TimeDelta tick_period_; const TimeDelta tick_period_;
RepeatingTaskHandle tick_task_; std::vector<absl::AnyInvocable<void() &&>> callbacks_;
bool started_ RTC_GUARDED_BY(mutex_) = false;
std::set<TickListener*> listeners_ RTC_GUARDED_BY(mutex_);
Mutex mutex_;
rtc::TaskQueue queue_;
}; };
} // namespace webrtc::test } // namespace webrtc::test

View File

@ -1037,11 +1037,13 @@ webrtc::VideoReceiveStreamInterface* Call::CreateVideoReceiveStream(
// and `video_receiver_controller_` out of VideoReceiveStream2 construction // and `video_receiver_controller_` out of VideoReceiveStream2 construction
// and set it up asynchronously on the network thread (the registration and // and set it up asynchronously on the network thread (the registration and
// `video_receiver_controller_` need to live on the network thread). // `video_receiver_controller_` need to live on the network thread).
// TODO(crbug.com/1381982): Re-enable decode synchronizer once the Chromium
// API has adapted to the new Metronome interface.
VideoReceiveStream2* receive_stream = new VideoReceiveStream2( VideoReceiveStream2* receive_stream = new VideoReceiveStream2(
task_queue_factory_, this, num_cpu_cores_, task_queue_factory_, this, num_cpu_cores_,
transport_send_->packet_router(), std::move(configuration), transport_send_->packet_router(), std::move(configuration),
call_stats_.get(), clock_, std::make_unique<VCMTiming>(clock_, trials()), call_stats_.get(), clock_, std::make_unique<VCMTiming>(clock_, trials()),
&nack_periodic_processor_, decode_sync_.get(), event_log_); &nack_periodic_processor_, /*decode_sync=*/nullptr, event_log_);
// TODO(bugs.webrtc.org/11993): Set this up asynchronously on the network // TODO(bugs.webrtc.org/11993): Set this up asynchronously on the network
// thread. // thread.
receive_stream->RegisterWithTransport(&video_receiver_controller_); receive_stream->RegisterWithTransport(&video_receiver_controller_);

View File

@ -110,6 +110,8 @@ PeerConnectionFactory::PeerConnectionFactory(
PeerConnectionFactory::~PeerConnectionFactory() { PeerConnectionFactory::~PeerConnectionFactory() {
RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK_RUN_ON(signaling_thread());
// Ensures the metronome is destroyed on the worker thread.
worker_thread()->BlockingCall([metronome = std::move(metronome_)] {});
} }
void PeerConnectionFactory::SetOptions(const Options& options) { void PeerConnectionFactory::SetOptions(const Options& options) {

View File

@ -152,7 +152,7 @@ class PeerConnectionFactory : public PeerConnectionFactoryInterface {
std::unique_ptr<NetEqFactory> neteq_factory_; std::unique_ptr<NetEqFactory> neteq_factory_;
const std::unique_ptr<RtpTransportControllerSendFactoryInterface> const std::unique_ptr<RtpTransportControllerSendFactoryInterface>
transport_controller_send_factory_; transport_controller_send_factory_;
std::unique_ptr<Metronome> metronome_; std::unique_ptr<Metronome> metronome_ RTC_GUARDED_BY(worker_thread());
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -56,41 +56,25 @@ int FindFirstMediaStatsIndexByKind(
return -1; return -1;
} }
TaskQueueMetronome::TaskQueueMetronome(TaskQueueFactory* factory, TaskQueueMetronome::TaskQueueMetronome(TimeDelta tick_period)
TimeDelta tick_period) : tick_period_(tick_period) {}
: tick_period_(tick_period),
queue_(factory->CreateTaskQueue("MetronomeQueue",
TaskQueueFactory::Priority::HIGH)) {
tick_task_ = RepeatingTaskHandle::Start(queue_.Get(), [this] {
MutexLock lock(&mutex_);
for (auto* listener : listeners_) {
listener->OnTickTaskQueue()->PostTask([listener] { listener->OnTick(); });
}
return tick_period_;
});
}
TaskQueueMetronome::~TaskQueueMetronome() { void TaskQueueMetronome::RequestCallOnNextTick(
RTC_DCHECK(listeners_.empty()); absl::AnyInvocable<void() &&> callback) {
rtc::Event stop_event; callbacks_.push_back(std::move(callback));
queue_.PostTask([this, &stop_event] { // Only schedule a tick callback for the first `callback` addition.
tick_task_.Stop(); // Schedule on the current task queue to comply with RequestCallOnNextTick
stop_event.Set(); // requirements.
}); if (callbacks_.size() == 1) {
stop_event.Wait(TimeDelta::Seconds(1)); TaskQueueBase::Current()->PostDelayedTask(
} [this] {
std::vector<absl::AnyInvocable<void() &&>> callbacks;
void TaskQueueMetronome::AddListener(TickListener* listener) { callbacks_.swap(callbacks);
MutexLock lock(&mutex_); for (auto& callback : callbacks)
auto [it, inserted] = listeners_.insert(listener); std::move(callback)();
RTC_DCHECK(inserted); },
} tick_period_);
}
void TaskQueueMetronome::RemoveListener(TickListener* listener) {
MutexLock lock(&mutex_);
auto it = listeners_.find(listener);
RTC_DCHECK(it != listeners_.end());
listeners_.erase(it);
} }
TimeDelta TaskQueueMetronome::TickPeriod() const { TimeDelta TaskQueueMetronome::TickPeriod() const {

View File

@ -180,20 +180,15 @@ int FindFirstMediaStatsIndexByKind(
class TaskQueueMetronome : public webrtc::Metronome { class TaskQueueMetronome : public webrtc::Metronome {
public: public:
TaskQueueMetronome(TaskQueueFactory* factory, TimeDelta tick_period); explicit TaskQueueMetronome(TimeDelta tick_period);
~TaskQueueMetronome() override;
// webrtc::Metronome implementation. // webrtc::Metronome implementation.
void AddListener(TickListener* listener) override; void RequestCallOnNextTick(absl::AnyInvocable<void() &&> callback) override;
void RemoveListener(TickListener* listener) override;
TimeDelta TickPeriod() const override; TimeDelta TickPeriod() const override;
private: private:
Mutex mutex_;
const TimeDelta tick_period_; const TimeDelta tick_period_;
std::set<TickListener*> listeners_ RTC_GUARDED_BY(mutex_); std::vector<absl::AnyInvocable<void() &&>> callbacks_;
RepeatingTaskHandle tick_task_;
rtc::TaskQueue queue_;
}; };
class SignalingMessageReceiver { class SignalingMessageReceiver {
@ -775,8 +770,8 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver,
pc_factory_dependencies.task_queue_factory = pc_factory_dependencies.task_queue_factory =
webrtc::CreateDefaultTaskQueueFactory(); webrtc::CreateDefaultTaskQueueFactory();
pc_factory_dependencies.trials = std::make_unique<FieldTrialBasedConfig>(); pc_factory_dependencies.trials = std::make_unique<FieldTrialBasedConfig>();
pc_factory_dependencies.metronome = std::make_unique<TaskQueueMetronome>( pc_factory_dependencies.metronome =
pc_factory_dependencies.task_queue_factory.get(), TimeDelta::Millis(8)); std::make_unique<TaskQueueMetronome>(TimeDelta::Millis(8));
cricket::MediaEngineDependencies media_deps; cricket::MediaEngineDependencies media_deps;
media_deps.task_queue_factory = media_deps.task_queue_factory =
pc_factory_dependencies.task_queue_factory.get(); pc_factory_dependencies.task_queue_factory.get();

View File

@ -106,6 +106,7 @@ DecodeSynchronizer::DecodeSynchronizer(Clock* clock,
} }
DecodeSynchronizer::~DecodeSynchronizer() { DecodeSynchronizer::~DecodeSynchronizer() {
RTC_DCHECK_RUN_ON(worker_queue_);
RTC_DCHECK(schedulers_.empty()); RTC_DCHECK(schedulers_.empty());
} }
@ -117,7 +118,7 @@ DecodeSynchronizer::CreateSynchronizedFrameScheduler() {
// If this is the first `scheduler` added, start listening to the metronome. // If this is the first `scheduler` added, start listening to the metronome.
if (inserted && schedulers_.size() == 1) { if (inserted && schedulers_.size() == 1) {
RTC_DLOG(LS_VERBOSE) << "Listening to metronome"; RTC_DLOG(LS_VERBOSE) << "Listening to metronome";
metronome_->AddListener(this); ScheduleNextTick();
} }
return std::move(scheduler); return std::move(scheduler);
@ -160,12 +161,16 @@ void DecodeSynchronizer::RemoveFrameScheduler(
schedulers_.erase(it); schedulers_.erase(it);
// If there are no more schedulers active, stop listening for metronome ticks. // If there are no more schedulers active, stop listening for metronome ticks.
if (schedulers_.empty()) { if (schedulers_.empty()) {
RTC_DLOG(LS_VERBOSE) << "Not listening to metronome";
metronome_->RemoveListener(this);
expected_next_tick_ = Timestamp::PlusInfinity(); expected_next_tick_ = Timestamp::PlusInfinity();
} }
} }
void DecodeSynchronizer::ScheduleNextTick() {
RTC_DCHECK_RUN_ON(worker_queue_);
metronome_->RequestCallOnNextTick(
SafeTask(safety_.flag(), [this] { OnTick(); }));
}
void DecodeSynchronizer::OnTick() { void DecodeSynchronizer::OnTick() {
RTC_DCHECK_RUN_ON(worker_queue_); RTC_DCHECK_RUN_ON(worker_queue_);
expected_next_tick_ = clock_->CurrentTime() + metronome_->TickPeriod(); expected_next_tick_ = clock_->CurrentTime() + metronome_->TickPeriod();
@ -177,10 +182,9 @@ void DecodeSynchronizer::OnTick() {
std::move(scheduled_frame).RunFrameReleaseCallback(); std::move(scheduled_frame).RunFrameReleaseCallback();
} }
} }
}
TaskQueueBase* DecodeSynchronizer::OnTickTaskQueue() { if (!schedulers_.empty())
return worker_queue_; ScheduleNextTick();
} }
} // namespace webrtc } // namespace webrtc

View File

@ -53,12 +53,12 @@ namespace webrtc {
// //
// DecodeSynchronizer is single threaded - all method calls must run on the // DecodeSynchronizer is single threaded - all method calls must run on the
// `worker_queue_`. // `worker_queue_`.
class DecodeSynchronizer : private Metronome::TickListener { class DecodeSynchronizer {
public: public:
DecodeSynchronizer(Clock* clock, DecodeSynchronizer(Clock* clock,
Metronome* metronome, Metronome* metronome,
TaskQueueBase* worker_queue); TaskQueueBase* worker_queue);
~DecodeSynchronizer() override; ~DecodeSynchronizer();
DecodeSynchronizer(const DecodeSynchronizer&) = delete; DecodeSynchronizer(const DecodeSynchronizer&) = delete;
DecodeSynchronizer& operator=(const DecodeSynchronizer&) = delete; DecodeSynchronizer& operator=(const DecodeSynchronizer&) = delete;
@ -119,9 +119,8 @@ class DecodeSynchronizer : private Metronome::TickListener {
void OnFrameScheduled(SynchronizedFrameDecodeScheduler* scheduler); void OnFrameScheduled(SynchronizedFrameDecodeScheduler* scheduler);
void RemoveFrameScheduler(SynchronizedFrameDecodeScheduler* scheduler); void RemoveFrameScheduler(SynchronizedFrameDecodeScheduler* scheduler);
// Metronome::TickListener implementation. void ScheduleNextTick();
void OnTick() override; void OnTick();
TaskQueueBase* OnTickTaskQueue() override;
Clock* const clock_; Clock* const clock_;
TaskQueueBase* const worker_queue_; TaskQueueBase* const worker_queue_;
@ -130,6 +129,7 @@ class DecodeSynchronizer : private Metronome::TickListener {
Timestamp expected_next_tick_ = Timestamp::PlusInfinity(); Timestamp expected_next_tick_ = Timestamp::PlusInfinity();
std::set<SynchronizedFrameDecodeScheduler*> schedulers_ std::set<SynchronizedFrameDecodeScheduler*> schedulers_
RTC_GUARDED_BY(worker_queue_); RTC_GUARDED_BY(worker_queue_);
ScopedTaskSafetyDetached safety_;
}; };
} // namespace webrtc } // namespace webrtc

View File

@ -15,6 +15,7 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include "absl/functional/any_invocable.h"
#include "api/metronome/test/fake_metronome.h" #include "api/metronome/test/fake_metronome.h"
#include "api/units/time_delta.h" #include "api/units/time_delta.h"
#include "test/gmock.h" #include "test/gmock.h"
@ -25,9 +26,20 @@
using ::testing::_; using ::testing::_;
using ::testing::Eq; using ::testing::Eq;
using ::testing::Invoke;
using ::testing::Return;
namespace webrtc { namespace webrtc {
class MockMetronome : public Metronome {
public:
MOCK_METHOD(void,
RequestCallOnNextTick,
(absl::AnyInvocable<void() &&> callback),
(override));
MOCK_METHOD(TimeDelta, TickPeriod, (), (const override));
};
class DecodeSynchronizerTest : public ::testing::Test { class DecodeSynchronizerTest : public ::testing::Test {
public: public:
static constexpr TimeDelta kTickPeriod = TimeDelta::Millis(33); static constexpr TimeDelta kTickPeriod = TimeDelta::Millis(33);
@ -215,18 +227,26 @@ TEST_F(DecodeSynchronizerTest, FramesNotReleasedAfterStop) {
time_controller_.AdvanceTime(TimeDelta::Zero()); time_controller_.AdvanceTime(TimeDelta::Zero());
} }
TEST_F(DecodeSynchronizerTest, MetronomeNotListenedWhenNoStreamsAreActive) { TEST(DecodeSynchronizerStandaloneTest,
EXPECT_EQ(0u, metronome_.NumListeners()); MetronomeNotListenedWhenNoStreamsAreActive) {
GlobalSimulatedTimeController time_controller(Timestamp::Millis(4711));
Clock* clock(time_controller.GetClock());
MockMetronome metronome;
ON_CALL(metronome, TickPeriod).WillByDefault(Return(TimeDelta::Seconds(1)));
DecodeSynchronizer decode_synchronizer_(clock, &metronome,
time_controller.GetMainThread());
absl::AnyInvocable<void() &&> callback;
EXPECT_CALL(metronome, RequestCallOnNextTick)
.WillOnce(Invoke([&callback](absl::AnyInvocable<void() &&> cb) {
callback = std::move(cb);
}));
auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler(); auto scheduler = decode_synchronizer_.CreateSynchronizedFrameScheduler();
EXPECT_EQ(1u, metronome_.NumListeners());
auto scheduler2 = decode_synchronizer_.CreateSynchronizedFrameScheduler(); auto scheduler2 = decode_synchronizer_.CreateSynchronizedFrameScheduler();
EXPECT_EQ(1u, metronome_.NumListeners());
scheduler->Stop(); scheduler->Stop();
EXPECT_EQ(1u, metronome_.NumListeners());
scheduler2->Stop(); scheduler2->Stop();
EXPECT_EQ(0u, metronome_.NumListeners()); time_controller.AdvanceTime(TimeDelta::Seconds(1));
ASSERT_TRUE(callback);
(std::move)(callback)();
} }
} // namespace webrtc } // namespace webrtc

View File

@ -196,8 +196,7 @@ class VideoReceiveStream2Test : public ::testing::TestWithParam<bool> {
config_(&mock_transport_, &mock_h264_decoder_factory_), config_(&mock_transport_, &mock_h264_decoder_factory_),
call_stats_(clock_, time_controller_.GetMainThread()), call_stats_(clock_, time_controller_.GetMainThread()),
fake_renderer_(&time_controller_), fake_renderer_(&time_controller_),
fake_metronome_(time_controller_.GetTaskQueueFactory(), fake_metronome_(TimeDelta::Millis(16)),
TimeDelta::Millis(16)),
decode_sync_(clock_, decode_sync_(clock_,
&fake_metronome_, &fake_metronome_,
time_controller_.GetMainThread()), time_controller_.GetMainThread()),
@ -228,7 +227,6 @@ class VideoReceiveStream2Test : public ::testing::TestWithParam<bool> {
video_receive_stream_->Stop(); video_receive_stream_->Stop();
video_receive_stream_->UnregisterFromTransport(); video_receive_stream_->UnregisterFromTransport();
} }
fake_metronome_.Stop();
time_controller_.AdvanceTime(TimeDelta::Zero()); time_controller_.AdvanceTime(TimeDelta::Zero());
} }

View File

@ -132,8 +132,7 @@ class VideoStreamBufferControllerFixture
field_trials_(std::get<1>(GetParam())), field_trials_(std::get<1>(GetParam())),
time_controller_(kClockStart), time_controller_(kClockStart),
clock_(time_controller_.GetClock()), clock_(time_controller_.GetClock()),
fake_metronome_(time_controller_.GetTaskQueueFactory(), fake_metronome_(TimeDelta::Millis(16)),
TimeDelta::Millis(16)),
decode_sync_(clock_, decode_sync_(clock_,
&fake_metronome_, &fake_metronome_,
time_controller_.GetMainThread()), time_controller_.GetMainThread()),
@ -163,7 +162,6 @@ class VideoStreamBufferControllerFixture
if (buffer_) { if (buffer_) {
buffer_->Stop(); buffer_->Stop();
} }
fake_metronome_.Stop();
time_controller_.AdvanceTime(TimeDelta::Zero()); time_controller_.AdvanceTime(TimeDelta::Zero());
} }