Delete video_legacy build target
Bug: None
Change-Id: I8c3e7cb408ca09b5e60f72b103764e2b43a4d696
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/265843
Commit-Queue: Danil Chapovalov <danilchap@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#37243}
diff --git a/BUILD.gn b/BUILD.gn
index 3ad9c97..461138a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -638,7 +638,6 @@
"test:test_common",
"test:test_main",
"test:video_test_common",
- "video:video_legacy_tests",
"video:video_tests",
"video/adaptation:video_adaptation_tests",
]
diff --git a/video/BUILD.gn b/video/BUILD.gn
index 381cad0..35e12e4 100644
--- a/video/BUILD.gn
+++ b/video/BUILD.gn
@@ -166,81 +166,6 @@
}
}
-rtc_source_set("video_legacy") {
- sources = [
- "call_stats.cc",
- "call_stats.h",
- "receive_statistics_proxy.cc",
- "receive_statistics_proxy.h",
- "video_quality_observer.cc",
- "video_quality_observer.h",
- ]
- deps = [
- ":frame_dumping_decoder",
- ":unique_timestamp_counter",
- ":video",
- "../api:array_view",
- "../api:field_trials_view",
- "../api:scoped_refptr",
- "../api:sequence_checker",
- "../api/crypto:frame_decryptor_interface",
- "../api/task_queue",
- "../api/transport:field_trial_based_config",
- "../api/units:timestamp",
- "../api/video:encoded_image",
- "../api/video:recordable_encoded_frame",
- "../api/video:video_frame",
- "../api/video:video_rtp_headers",
- "../call:call_interfaces",
- "../call:rtp_interfaces",
- "../call:rtp_receiver", # For RtxReceiveStream.
- "../call:video_stream_api",
- "../common_video",
- "../modules:module_api",
- "../modules/pacing",
- "../modules/remote_bitrate_estimator",
- "../modules/rtp_rtcp",
- "../modules/rtp_rtcp:rtp_rtcp_format",
- "../modules/rtp_rtcp:rtp_rtcp_legacy",
- "../modules/rtp_rtcp:rtp_video_header",
- "../modules/utility",
- "../modules/video_coding",
- "../modules/video_coding:packet_buffer",
- "../modules/video_coding:video_codec_interface",
- "../modules/video_coding:video_coding_utility",
- "../modules/video_coding/deprecated:nack_module",
- "../rtc_base:checks",
- "../rtc_base:histogram_percentile_counter",
- "../rtc_base:location",
- "../rtc_base:logging",
- "../rtc_base:macromagic",
- "../rtc_base:moving_max_counter",
- "../rtc_base:platform_thread",
- "../rtc_base:rate_statistics",
- "../rtc_base:rate_tracker",
- "../rtc_base:rtc_numerics",
- "../rtc_base:rtc_task_queue",
- "../rtc_base:sample_counter",
- "../rtc_base:stringutils",
- "../rtc_base:timeutils",
- "../rtc_base/experiments:field_trial_parser",
- "../rtc_base/experiments:keyframe_interval_settings_experiment",
- "../rtc_base/synchronization:mutex",
- "../rtc_base/system:no_unique_address",
- "../rtc_base/task_utils:to_queued_task",
- "../system_wrappers",
- "../system_wrappers:field_trial",
- "../system_wrappers:metrics",
- ]
- if (!build_with_mozilla) {
- deps += [ "../media:rtc_media_base" ]
- }
- absl_deps = [
- "//third_party/abseil-cpp/absl/algorithm:container",
- "//third_party/abseil-cpp/absl/types:optional",
- ]
-}
-
rtc_library("video_stream_decoder_impl") {
visibility = [ "*" ]
@@ -1001,40 +926,4 @@
deps += [ "../media:rtc_media_base" ]
}
}
- rtc_library("video_legacy_tests") {
- testonly = true
- sources = [
- "call_stats_unittest.cc",
- "receive_statistics_proxy_unittest.cc",
- ]
- deps = [
- ":video_legacy",
- "../api:scoped_refptr",
- "../api/video:video_frame",
- "../api/video:video_frame_type",
- "../api/video:video_rtp_headers",
- "../call:mock_rtp_interfaces",
- "../common_video",
- "../media:rtc_media_base",
- "../modules/rtp_rtcp",
- "../modules/rtp_rtcp:rtp_rtcp_format",
- "../modules/utility",
- "../modules/video_coding",
- "../modules/video_coding:video_codec_interface",
- "../rtc_base:byte_buffer",
- "../rtc_base:location",
- "../rtc_base:logging",
- "../rtc_base:rtc_event",
- "../rtc_base/task_utils:to_queued_task",
- "../system_wrappers",
- "../system_wrappers:field_trial",
- "../system_wrappers:metrics",
- "../test:field_trial",
- "../test:mock_frame_transformer",
- "../test:mock_transport",
- "../test:scoped_key_value_config",
- "../test:test_support",
- ]
- absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
- }
}
diff --git a/video/call_stats.cc b/video/call_stats.cc
deleted file mode 100644
index 9fd6802..0000000
--- a/video/call_stats.cc
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (c) 2012 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/call_stats.h"
-
-#include <algorithm>
-#include <memory>
-
-#include "absl/algorithm/container.h"
-#include "modules/utility/include/process_thread.h"
-#include "rtc_base/checks.h"
-#include "rtc_base/location.h"
-#include "rtc_base/task_utils/to_queued_task.h"
-#include "system_wrappers/include/metrics.h"
-
-namespace webrtc {
-namespace {
-
-void RemoveOldReports(int64_t now, std::list<CallStats::RttTime>* reports) {
- static constexpr const int64_t kRttTimeoutMs = 1500;
- reports->remove_if(
- [&now](CallStats::RttTime& r) { return now - r.time > kRttTimeoutMs; });
-}
-
-int64_t GetMaxRttMs(const std::list<CallStats::RttTime>& reports) {
- int64_t max_rtt_ms = -1;
- for (const CallStats::RttTime& rtt_time : reports)
- max_rtt_ms = std::max(rtt_time.rtt, max_rtt_ms);
- return max_rtt_ms;
-}
-
-int64_t GetAvgRttMs(const std::list<CallStats::RttTime>& reports) {
- RTC_DCHECK(!reports.empty());
- int64_t sum = 0;
- for (std::list<CallStats::RttTime>::const_iterator it = reports.begin();
- it != reports.end(); ++it) {
- sum += it->rtt;
- }
- return sum / reports.size();
-}
-
-int64_t GetNewAvgRttMs(const std::list<CallStats::RttTime>& reports,
- int64_t prev_avg_rtt) {
- if (reports.empty())
- return -1; // Reset (invalid average).
-
- int64_t cur_rtt_ms = GetAvgRttMs(reports);
- if (prev_avg_rtt == -1)
- return cur_rtt_ms; // New initial average value.
-
- // Weight factor to apply to the average rtt.
- // We weigh the old average at 70% against the new average (30%).
- constexpr const float kWeightFactor = 0.3f;
- return prev_avg_rtt * (1.0f - kWeightFactor) + cur_rtt_ms * kWeightFactor;
-}
-
-// This class is used to de-register a Module from a ProcessThread to satisfy
-// threading requirements of the Module (CallStats).
-// The guarantee offered by TemporaryDeregistration is that while its in scope,
-// no calls to `TimeUntilNextProcess` or `Process()` will occur and therefore
-// synchronization with those methods, is not necessary.
-class TemporaryDeregistration {
- public:
- TemporaryDeregistration(Module* module,
- ProcessThread* process_thread,
- bool thread_running)
- : module_(module),
- process_thread_(process_thread),
- deregistered_(thread_running) {
- if (thread_running)
- process_thread_->DeRegisterModule(module_);
- }
- ~TemporaryDeregistration() {
- if (deregistered_)
- process_thread_->RegisterModule(module_, RTC_FROM_HERE);
- }
-
- private:
- Module* const module_;
- ProcessThread* const process_thread_;
- const bool deregistered_;
-};
-
-} // namespace
-
-CallStats::CallStats(Clock* clock, ProcessThread* process_thread)
- : clock_(clock),
- last_process_time_(clock_->TimeInMilliseconds()),
- max_rtt_ms_(-1),
- avg_rtt_ms_(-1),
- sum_avg_rtt_ms_(0),
- num_avg_rtt_(0),
- time_of_first_rtt_ms_(-1),
- process_thread_(process_thread),
- process_thread_running_(false) {
- RTC_DCHECK(process_thread_);
- process_thread_checker_.Detach();
-}
-
-CallStats::~CallStats() {
- RTC_DCHECK_RUN_ON(&construction_thread_checker_);
- RTC_DCHECK(!process_thread_running_);
- RTC_DCHECK(observers_.empty());
-
- UpdateHistograms();
-}
-
-int64_t CallStats::TimeUntilNextProcess() {
- RTC_DCHECK_RUN_ON(&process_thread_checker_);
- return last_process_time_ + kUpdateIntervalMs - clock_->TimeInMilliseconds();
-}
-
-void CallStats::Process() {
- RTC_DCHECK_RUN_ON(&process_thread_checker_);
- int64_t now = clock_->TimeInMilliseconds();
- last_process_time_ = now;
-
- // `avg_rtt_ms_` is allowed to be read on the process thread since that's the
- // only thread that modifies the value.
- int64_t avg_rtt_ms = avg_rtt_ms_;
- RemoveOldReports(now, &reports_);
- max_rtt_ms_ = GetMaxRttMs(reports_);
- avg_rtt_ms = GetNewAvgRttMs(reports_, avg_rtt_ms);
- {
- MutexLock lock(&avg_rtt_ms_lock_);
- avg_rtt_ms_ = avg_rtt_ms;
- }
-
- // If there is a valid rtt, update all observers with the max rtt.
- if (max_rtt_ms_ >= 0) {
- RTC_DCHECK_GE(avg_rtt_ms, 0);
- for (CallStatsObserver* observer : observers_)
- observer->OnRttUpdate(avg_rtt_ms, max_rtt_ms_);
- // Sum for Histogram of average RTT reported over the entire call.
- sum_avg_rtt_ms_ += avg_rtt_ms;
- ++num_avg_rtt_;
- }
-}
-
-void CallStats::ProcessThreadAttached(ProcessThread* process_thread) {
- RTC_DCHECK_RUN_ON(&construction_thread_checker_);
- RTC_DCHECK(!process_thread || process_thread_ == process_thread);
- process_thread_running_ = process_thread != nullptr;
-
- // Whether we just got attached or detached, we clear the
- // `process_thread_checker_` so that it can be used to protect variables
- // in either the process thread when it starts again, or UpdateHistograms()
- // (mutually exclusive).
- process_thread_checker_.Detach();
-}
-
-void CallStats::RegisterStatsObserver(CallStatsObserver* observer) {
- RTC_DCHECK_RUN_ON(&construction_thread_checker_);
- TemporaryDeregistration deregister(this, process_thread_,
- process_thread_running_);
-
- if (!absl::c_linear_search(observers_, observer))
- observers_.push_back(observer);
-}
-
-void CallStats::DeregisterStatsObserver(CallStatsObserver* observer) {
- RTC_DCHECK_RUN_ON(&construction_thread_checker_);
- TemporaryDeregistration deregister(this, process_thread_,
- process_thread_running_);
- observers_.remove(observer);
-}
-
-int64_t CallStats::LastProcessedRtt() const {
- // TODO(tommi): This currently gets called from the construction thread of
- // Call as well as from the process thread. Look into restricting this to
- // allow only reading this from the process thread (or TQ once we get there)
- // so that the lock isn't necessary.
-
- MutexLock lock(&avg_rtt_ms_lock_);
- return avg_rtt_ms_;
-}
-
-void CallStats::OnRttUpdate(int64_t rtt) {
- RTC_DCHECK_RUN_ON(&process_thread_checker_);
-
- int64_t now_ms = clock_->TimeInMilliseconds();
- reports_.push_back(RttTime(rtt, now_ms));
- if (time_of_first_rtt_ms_ == -1)
- time_of_first_rtt_ms_ = now_ms;
-
- // Make sure Process() will be called and deliver the updates asynchronously.
- last_process_time_ -= kUpdateIntervalMs;
- process_thread_->WakeUp(this);
-}
-
-void CallStats::UpdateHistograms() {
- RTC_DCHECK_RUN_ON(&construction_thread_checker_);
- RTC_DCHECK(!process_thread_running_);
-
- // The extra scope is because we have two 'dcheck run on' thread checkers.
- // This is a special case since it's safe to access variables on the current
- // thread that normally are only touched on the process thread.
- // Since we're not attached to the process thread and/or the process thread
- // isn't running, it's OK to touch these variables here.
- {
- // This method is called on the ctor thread (usually from the dtor, unless
- // a test calls it). It's a requirement that the function be called when
- // the process thread is not running (a condition that's met at destruction
- // time), and thanks to that, we don't need a lock to synchronize against
- // it.
- RTC_DCHECK_RUN_ON(&process_thread_checker_);
-
- if (time_of_first_rtt_ms_ == -1 || num_avg_rtt_ < 1)
- return;
-
- int64_t elapsed_sec =
- (clock_->TimeInMilliseconds() - time_of_first_rtt_ms_) / 1000;
- if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
- int64_t avg_rtt_ms = (sum_avg_rtt_ms_ + num_avg_rtt_ / 2) / num_avg_rtt_;
- RTC_HISTOGRAM_COUNTS_10000(
- "WebRTC.Video.AverageRoundTripTimeInMilliseconds", avg_rtt_ms);
- }
- }
-}
-
-} // namespace webrtc
diff --git a/video/call_stats.h b/video/call_stats.h
deleted file mode 100644
index d198223..0000000
--- a/video/call_stats.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef VIDEO_CALL_STATS_H_
-#define VIDEO_CALL_STATS_H_
-
-#include <list>
-#include <memory>
-
-#include "api/sequence_checker.h"
-#include "modules/include/module.h"
-#include "modules/include/module_common_types.h"
-#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
-#include "rtc_base/synchronization/mutex.h"
-#include "system_wrappers/include/clock.h"
-
-namespace webrtc {
-
-// CallStats keeps track of statistics for a call.
-// TODO(webrtc:11489): Make call_stats_ not depend on ProcessThread and
-// make callbacks on the worker thread (TQ).
-class CallStats : public Module, public RtcpRttStats {
- public:
- // Time interval for updating the observers.
- static constexpr int64_t kUpdateIntervalMs = 1000;
-
- CallStats(Clock* clock, ProcessThread* process_thread);
- ~CallStats() override;
-
- CallStats(const CallStats&) = delete;
- CallStats& operator=(const CallStats&) = delete;
-
- // Registers/deregisters a new observer to receive statistics updates.
- // Must be called from the construction thread.
- void RegisterStatsObserver(CallStatsObserver* observer);
- void DeregisterStatsObserver(CallStatsObserver* observer);
-
- // Expose `LastProcessedRtt()` from RtcpRttStats to the public interface, as
- // it is the part of the API that is needed by direct users of CallStats.
- // TODO(tommi): Threading or lifetime guarantees are not explicit in how
- // CallStats is used as RtcpRttStats or how pointers are cached in a
- // few different places (distributed via Call). It would be good to clarify
- // from what thread/TQ calls to OnRttUpdate and LastProcessedRtt need to be
- // allowed.
- int64_t LastProcessedRtt() const override;
-
- // Exposed for tests to test histogram support.
- void UpdateHistogramsForTest() { UpdateHistograms(); }
-
- // Helper struct keeping track of the time a rtt value is reported.
- struct RttTime {
- RttTime(int64_t new_rtt, int64_t rtt_time) : rtt(new_rtt), time(rtt_time) {}
- const int64_t rtt;
- const int64_t time;
- };
-
- private:
- // RtcpRttStats implementation.
- void OnRttUpdate(int64_t rtt) override;
-
- // Implements Module, to use the process thread.
- int64_t TimeUntilNextProcess() override;
- void Process() override;
-
- // TODO(tommi): Use this to know when we're attached to the process thread?
- // Alternatively, inject that pointer via the ctor since the call_stats
- // test code, isn't using a processthread atm.
- void ProcessThreadAttached(ProcessThread* process_thread) override;
-
- // This method must only be called when the process thread is not
- // running, and from the construction thread.
- void UpdateHistograms();
-
- Clock* const clock_;
-
- // The last time 'Process' resulted in statistic update.
- int64_t last_process_time_ RTC_GUARDED_BY(process_thread_checker_);
- // The last RTT in the statistics update (zero if there is no valid estimate).
- int64_t max_rtt_ms_ RTC_GUARDED_BY(process_thread_checker_);
-
- // Accessed from random threads (seemingly). Consider atomic.
- // `avg_rtt_ms_` is allowed to be read on the process thread without a lock.
- // `avg_rtt_ms_lock_` must be held elsewhere for reading.
- // `avg_rtt_ms_lock_` must be held on the process thread for writing.
- int64_t avg_rtt_ms_;
-
- // Protects `avg_rtt_ms_`.
- mutable Mutex avg_rtt_ms_lock_;
-
- // `sum_avg_rtt_ms_`, `num_avg_rtt_` and `time_of_first_rtt_ms_` are only used
- // on the ProcessThread when running. When the Process Thread is not running,
- // (and only then) they can be used in UpdateHistograms(), usually called from
- // the dtor.
- int64_t sum_avg_rtt_ms_ RTC_GUARDED_BY(process_thread_checker_);
- int64_t num_avg_rtt_ RTC_GUARDED_BY(process_thread_checker_);
- int64_t time_of_first_rtt_ms_ RTC_GUARDED_BY(process_thread_checker_);
-
- // All Rtt reports within valid time interval, oldest first.
- std::list<RttTime> reports_ RTC_GUARDED_BY(process_thread_checker_);
-
- // Observers getting stats reports.
- // When attached to ProcessThread, this is read-only. In order to allow
- // modification, we detach from the process thread while the observer
- // list is updated, to avoid races. This allows us to not require a lock
- // for the observers_ list, which makes the most common case lock free.
- std::list<CallStatsObserver*> observers_;
-
- SequenceChecker construction_thread_checker_;
- SequenceChecker process_thread_checker_;
- ProcessThread* const process_thread_;
- bool process_thread_running_ RTC_GUARDED_BY(construction_thread_checker_);
-};
-
-} // namespace webrtc
-
-#endif // VIDEO_CALL_STATS_H_
diff --git a/video/call_stats_unittest.cc b/video/call_stats_unittest.cc
deleted file mode 100644
index e85c4f8..0000000
--- a/video/call_stats_unittest.cc
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Copyright (c) 2012 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/call_stats.h"
-
-#include <memory>
-
-#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
-#include "modules/utility/include/process_thread.h"
-#include "rtc_base/event.h"
-#include "rtc_base/location.h"
-#include "rtc_base/task_utils/to_queued_task.h"
-#include "system_wrappers/include/metrics.h"
-#include "test/gmock.h"
-#include "test/gtest.h"
-
-using ::testing::AnyNumber;
-using ::testing::InvokeWithoutArgs;
-using ::testing::Return;
-
-namespace webrtc {
-
-class MockStatsObserver : public CallStatsObserver {
- public:
- MockStatsObserver() {}
- virtual ~MockStatsObserver() {}
-
- MOCK_METHOD(void, OnRttUpdate, (int64_t, int64_t), (override));
-};
-
-class CallStatsTest : public ::testing::Test {
- public:
- CallStatsTest() {
- process_thread_->RegisterModule(&call_stats_, RTC_FROM_HERE);
- process_thread_->Start();
- }
- ~CallStatsTest() override {
- process_thread_->Stop();
- process_thread_->DeRegisterModule(&call_stats_);
- }
-
- // Queues an rtt update call on the process thread.
- void AsyncSimulateRttUpdate(int64_t rtt) {
- RtcpRttStats* rtcp_rtt_stats = &call_stats_;
- process_thread_->PostTask(ToQueuedTask(
- [rtcp_rtt_stats, rtt] { rtcp_rtt_stats->OnRttUpdate(rtt); }));
- }
-
- protected:
- std::unique_ptr<ProcessThread> process_thread_{
- ProcessThread::Create("CallStats")};
- SimulatedClock fake_clock_{12345};
- CallStats call_stats_{&fake_clock_, process_thread_.get()};
-};
-
-TEST_F(CallStatsTest, AddAndTriggerCallback) {
- rtc::Event event;
-
- static constexpr const int64_t kRtt = 25;
-
- MockStatsObserver stats_observer;
- EXPECT_CALL(stats_observer, OnRttUpdate(kRtt, kRtt))
- .Times(1)
- .WillOnce(InvokeWithoutArgs([&event] { event.Set(); }));
-
- RtcpRttStats* rtcp_rtt_stats = &call_stats_;
- call_stats_.RegisterStatsObserver(&stats_observer);
- EXPECT_EQ(-1, rtcp_rtt_stats->LastProcessedRtt());
-
- AsyncSimulateRttUpdate(kRtt);
-
- EXPECT_TRUE(event.Wait(1000));
-
- EXPECT_EQ(kRtt, rtcp_rtt_stats->LastProcessedRtt());
-
- call_stats_.DeregisterStatsObserver(&stats_observer);
-}
-
-TEST_F(CallStatsTest, ProcessTime) {
- rtc::Event event;
-
- static constexpr const int64_t kRtt = 100;
- static constexpr const int64_t kRtt2 = 80;
-
- RtcpRttStats* rtcp_rtt_stats = &call_stats_;
-
- MockStatsObserver stats_observer;
-
- EXPECT_CALL(stats_observer, OnRttUpdate(kRtt, kRtt))
- .Times(2)
- .WillOnce(InvokeWithoutArgs([this] {
- // Advance clock and verify we get an update.
- fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateIntervalMs);
- }))
- .WillRepeatedly(InvokeWithoutArgs([this, rtcp_rtt_stats] {
- rtcp_rtt_stats->OnRttUpdate(kRtt2);
- // Advance clock just too little to get an update.
- fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateIntervalMs - 1);
- }));
-
- // In case you're reading this and wondering how this number is arrived at,
- // please see comments in the ChangeRtt test that go into some detail.
- static constexpr const int64_t kLastAvg = 94;
- EXPECT_CALL(stats_observer, OnRttUpdate(kLastAvg, kRtt2))
- .Times(1)
- .WillOnce(InvokeWithoutArgs([&event] { event.Set(); }));
-
- call_stats_.RegisterStatsObserver(&stats_observer);
-
- AsyncSimulateRttUpdate(kRtt);
- EXPECT_TRUE(event.Wait(1000));
-
- call_stats_.DeregisterStatsObserver(&stats_observer);
-}
-
-// Verify all observers get correct estimates and observers can be added and
-// removed.
-TEST_F(CallStatsTest, MultipleObservers) {
- MockStatsObserver stats_observer_1;
- call_stats_.RegisterStatsObserver(&stats_observer_1);
- // Add the second observer twice, there should still be only one report to the
- // observer.
- MockStatsObserver stats_observer_2;
- call_stats_.RegisterStatsObserver(&stats_observer_2);
- call_stats_.RegisterStatsObserver(&stats_observer_2);
-
- static constexpr const int64_t kRtt = 100;
-
- // Verify both observers are updated.
- rtc::Event ev1;
- rtc::Event ev2;
- EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt, kRtt))
- .Times(AnyNumber())
- .WillOnce(InvokeWithoutArgs([&ev1] { ev1.Set(); }))
- .WillRepeatedly(Return());
- EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt, kRtt))
- .Times(AnyNumber())
- .WillOnce(InvokeWithoutArgs([&ev2] { ev2.Set(); }))
- .WillRepeatedly(Return());
- AsyncSimulateRttUpdate(kRtt);
- ASSERT_TRUE(ev1.Wait(100));
- ASSERT_TRUE(ev2.Wait(100));
-
- // Deregister the second observer and verify update is only sent to the first
- // observer.
- call_stats_.DeregisterStatsObserver(&stats_observer_2);
-
- EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt, kRtt))
- .Times(AnyNumber())
- .WillOnce(InvokeWithoutArgs([&ev1] { ev1.Set(); }))
- .WillRepeatedly(Return());
- EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt, kRtt)).Times(0);
- AsyncSimulateRttUpdate(kRtt);
- ASSERT_TRUE(ev1.Wait(100));
-
- // Deregister the first observer.
- call_stats_.DeregisterStatsObserver(&stats_observer_1);
-
- // Now make sure we don't get any callbacks.
- EXPECT_CALL(stats_observer_1, OnRttUpdate(kRtt, kRtt)).Times(0);
- EXPECT_CALL(stats_observer_2, OnRttUpdate(kRtt, kRtt)).Times(0);
- AsyncSimulateRttUpdate(kRtt);
-
- // Force a call to Process().
- process_thread_->WakeUp(&call_stats_);
-
- // Flush the queue on the process thread to make sure we return after
- // Process() has been called.
- rtc::Event event;
- process_thread_->PostTask(ToQueuedTask([&event] { event.Set(); }));
- event.Wait(rtc::Event::kForever);
-}
-
-// Verify increasing and decreasing rtt triggers callbacks with correct values.
-TEST_F(CallStatsTest, ChangeRtt) {
- // TODO(tommi): This test assumes things about how old reports are removed
- // inside of call_stats.cc. The threshold ms value is 1500ms, but it's not
- // clear here that how the clock is advanced, affects that algorithm and
- // subsequently the average reported rtt.
-
- MockStatsObserver stats_observer;
- call_stats_.RegisterStatsObserver(&stats_observer);
- RtcpRttStats* rtcp_rtt_stats = &call_stats_;
-
- rtc::Event event;
-
- static constexpr const int64_t kFirstRtt = 100;
- static constexpr const int64_t kLowRtt = kFirstRtt - 20;
- static constexpr const int64_t kHighRtt = kFirstRtt + 20;
-
- EXPECT_CALL(stats_observer, OnRttUpdate(kFirstRtt, kFirstRtt))
- .Times(1)
- .WillOnce(InvokeWithoutArgs([&rtcp_rtt_stats, this] {
- fake_clock_.AdvanceTimeMilliseconds(1000);
- rtcp_rtt_stats->OnRttUpdate(kHighRtt); // Reported at T1 (1000ms).
- }));
-
- // TODO(tommi): This relies on the internal algorithms of call_stats.cc.
- // There's a weight factor there (0.3), that weighs the previous average to
- // the new one by 70%, so the number 103 in this case is arrived at like so:
- // (100) / 1 * 0.7 + (100+120)/2 * 0.3 = 103
- static constexpr const int64_t kAvgRtt1 = 103;
- EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt1, kHighRtt))
- .Times(1)
- .WillOnce(InvokeWithoutArgs([&rtcp_rtt_stats, this] {
- // This interacts with an internal implementation detail in call_stats
- // that decays the oldest rtt value. See more below.
- fake_clock_.AdvanceTimeMilliseconds(1000);
- rtcp_rtt_stats->OnRttUpdate(kLowRtt); // Reported at T2 (2000ms).
- }));
-
- // Increase time enough for a new update, but not too much to make the
- // rtt invalid. Report a lower rtt and verify the old/high value still is sent
- // in the callback.
-
- // Here, enough time must have passed in order to remove exactly the first
- // report and nothing else (>1500ms has passed since the first rtt).
- // So, this value is arrived by doing:
- // (kAvgRtt1)/1 * 0.7 + (kHighRtt+kLowRtt)/2 * 0.3 = 102.1
- static constexpr const int64_t kAvgRtt2 = 102;
- EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt2, kHighRtt))
- .Times(1)
- .WillOnce(InvokeWithoutArgs([this] {
- // Advance time to make the high report invalid, the lower rtt should
- // now be in the callback.
- fake_clock_.AdvanceTimeMilliseconds(1000);
- }));
-
- static constexpr const int64_t kAvgRtt3 = 95;
- EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt3, kLowRtt))
- .Times(1)
- .WillOnce(InvokeWithoutArgs([&event] { event.Set(); }));
-
- // Trigger the first rtt value and set off the chain of callbacks.
- AsyncSimulateRttUpdate(kFirstRtt); // Reported at T0 (0ms).
- EXPECT_TRUE(event.Wait(1000));
-
- call_stats_.DeregisterStatsObserver(&stats_observer);
-}
-
-TEST_F(CallStatsTest, LastProcessedRtt) {
- rtc::Event event;
- MockStatsObserver stats_observer;
- call_stats_.RegisterStatsObserver(&stats_observer);
- RtcpRttStats* rtcp_rtt_stats = &call_stats_;
-
- static constexpr const int64_t kRttLow = 10;
- static constexpr const int64_t kRttHigh = 30;
- // The following two average numbers dependend on average + weight
- // calculations in call_stats.cc.
- static constexpr const int64_t kAvgRtt1 = 13;
- static constexpr const int64_t kAvgRtt2 = 15;
-
- EXPECT_CALL(stats_observer, OnRttUpdate(kRttLow, kRttLow))
- .Times(1)
- .WillOnce(InvokeWithoutArgs([rtcp_rtt_stats] {
- EXPECT_EQ(kRttLow, rtcp_rtt_stats->LastProcessedRtt());
- // Don't advance the clock to make sure that low and high rtt values
- // are associated with the same time stamp.
- rtcp_rtt_stats->OnRttUpdate(kRttHigh);
- }));
-
- EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt1, kRttHigh))
- .Times(1)
- .WillOnce(InvokeWithoutArgs([rtcp_rtt_stats, this] {
- EXPECT_EQ(kAvgRtt1, rtcp_rtt_stats->LastProcessedRtt());
- fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateIntervalMs);
- rtcp_rtt_stats->OnRttUpdate(kRttLow);
- rtcp_rtt_stats->OnRttUpdate(kRttHigh);
- }));
-
- EXPECT_CALL(stats_observer, OnRttUpdate(kAvgRtt2, kRttHigh))
- .Times(1)
- .WillOnce(InvokeWithoutArgs([rtcp_rtt_stats, &event] {
- EXPECT_EQ(kAvgRtt2, rtcp_rtt_stats->LastProcessedRtt());
- event.Set();
- }));
-
- // Set a first values and verify that LastProcessedRtt initially returns the
- // average rtt.
- fake_clock_.AdvanceTimeMilliseconds(CallStats::kUpdateIntervalMs);
- AsyncSimulateRttUpdate(kRttLow);
- EXPECT_TRUE(event.Wait(1000));
- EXPECT_EQ(kAvgRtt2, rtcp_rtt_stats->LastProcessedRtt());
-
- call_stats_.DeregisterStatsObserver(&stats_observer);
-}
-
-TEST_F(CallStatsTest, ProducesHistogramMetrics) {
- metrics::Reset();
- rtc::Event event;
- static constexpr const int64_t kRtt = 123;
- MockStatsObserver stats_observer;
- call_stats_.RegisterStatsObserver(&stats_observer);
- EXPECT_CALL(stats_observer, OnRttUpdate(kRtt, kRtt))
- .Times(AnyNumber())
- .WillRepeatedly(InvokeWithoutArgs([&event] { event.Set(); }));
-
- AsyncSimulateRttUpdate(kRtt);
- EXPECT_TRUE(event.Wait(1000));
- fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds *
- CallStats::kUpdateIntervalMs);
- AsyncSimulateRttUpdate(kRtt);
- EXPECT_TRUE(event.Wait(1000));
-
- call_stats_.DeregisterStatsObserver(&stats_observer);
-
- process_thread_->Stop();
- call_stats_.UpdateHistogramsForTest();
-
- EXPECT_METRIC_EQ(1, metrics::NumSamples(
- "WebRTC.Video.AverageRoundTripTimeInMilliseconds"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.AverageRoundTripTimeInMilliseconds",
- kRtt));
-}
-
-} // namespace webrtc
diff --git a/video/receive_statistics_proxy.cc b/video/receive_statistics_proxy.cc
deleted file mode 100644
index 06873d8..0000000
--- a/video/receive_statistics_proxy.cc
+++ /dev/null
@@ -1,945 +0,0 @@
-/*
- * Copyright (c) 2013 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/receive_statistics_proxy.h"
-
-#include <algorithm>
-#include <cmath>
-#include <utility>
-
-#include "modules/video_coding/include/video_codec_interface.h"
-#include "rtc_base/checks.h"
-#include "rtc_base/logging.h"
-#include "rtc_base/strings/string_builder.h"
-#include "rtc_base/time_utils.h"
-#include "system_wrappers/include/clock.h"
-#include "system_wrappers/include/metrics.h"
-
-namespace webrtc {
-namespace {
-// Periodic time interval for processing samples for `freq_offset_counter_`.
-const int64_t kFreqOffsetProcessIntervalMs = 40000;
-
-// Configuration for bad call detection.
-const int kBadCallMinRequiredSamples = 10;
-const int kMinSampleLengthMs = 990;
-const int kNumMeasurements = 10;
-const int kNumMeasurementsVariance = kNumMeasurements * 1.5;
-const float kBadFraction = 0.8f;
-// For fps:
-// Low means low enough to be bad, high means high enough to be good
-const int kLowFpsThreshold = 12;
-const int kHighFpsThreshold = 14;
-// For qp and fps variance:
-// Low means low enough to be good, high means high enough to be bad
-const int kLowQpThresholdVp8 = 60;
-const int kHighQpThresholdVp8 = 70;
-const int kLowVarianceThreshold = 1;
-const int kHighVarianceThreshold = 2;
-
-// Some metrics are reported as a maximum over this period.
-// This should be synchronized with a typical getStats polling interval in
-// the clients.
-const int kMovingMaxWindowMs = 1000;
-
-// How large window we use to calculate the framerate/bitrate.
-const int kRateStatisticsWindowSizeMs = 1000;
-
-// Some sane ballpark estimate for maximum common value of inter-frame delay.
-// Values below that will be stored explicitly in the array,
-// values above - in the map.
-const int kMaxCommonInterframeDelayMs = 500;
-
-const char* UmaPrefixForContentType(VideoContentType content_type) {
- if (videocontenttypehelpers::IsScreenshare(content_type))
- return "WebRTC.Video.Screenshare";
- return "WebRTC.Video";
-}
-
-std::string UmaSuffixForContentType(VideoContentType content_type) {
- char ss_buf[1024];
- rtc::SimpleStringBuilder ss(ss_buf);
- int simulcast_id = videocontenttypehelpers::GetSimulcastId(content_type);
- if (simulcast_id > 0) {
- ss << ".S" << simulcast_id - 1;
- }
- int experiment_id = videocontenttypehelpers::GetExperimentId(content_type);
- if (experiment_id > 0) {
- ss << ".ExperimentGroup" << experiment_id - 1;
- }
- return ss.str();
-}
-
-bool EnableDecodeTimeHistogram(const FieldTrialsView* field_trials) {
- if (field_trials == nullptr) {
- return true;
- }
- return !field_trials->IsEnabled("WebRTC-DecodeTimeHistogramsKillSwitch");
-}
-
-} // namespace
-
-ReceiveStatisticsProxy::ReceiveStatisticsProxy(
- uint32_t remote_ssrc,
- Clock* clock,
- const FieldTrialsView* field_trials)
- : clock_(clock),
- start_ms_(clock->TimeInMilliseconds()),
- enable_decode_time_histograms_(EnableDecodeTimeHistogram(field_trials)),
- last_sample_time_(clock->TimeInMilliseconds()),
- fps_threshold_(kLowFpsThreshold,
- kHighFpsThreshold,
- kBadFraction,
- kNumMeasurements),
- qp_threshold_(kLowQpThresholdVp8,
- kHighQpThresholdVp8,
- kBadFraction,
- kNumMeasurements),
- variance_threshold_(kLowVarianceThreshold,
- kHighVarianceThreshold,
- kBadFraction,
- kNumMeasurementsVariance),
- num_bad_states_(0),
- num_certain_states_(0),
- // 1000ms window, scale 1000 for ms to s.
- decode_fps_estimator_(1000, 1000),
- renders_fps_estimator_(1000, 1000),
- render_fps_tracker_(100, 10u),
- render_pixel_tracker_(100, 10u),
- video_quality_observer_(
- new VideoQualityObserver(VideoContentType::UNSPECIFIED)),
- interframe_delay_max_moving_(kMovingMaxWindowMs),
- freq_offset_counter_(clock, nullptr, kFreqOffsetProcessIntervalMs),
- avg_rtt_ms_(0),
- last_content_type_(VideoContentType::UNSPECIFIED),
- last_codec_type_(kVideoCodecVP8),
- num_delayed_frames_rendered_(0),
- sum_missed_render_deadline_ms_(0),
- timing_frame_info_counter_(kMovingMaxWindowMs) {
- decode_thread_.Detach();
- network_thread_.Detach();
- stats_.ssrc = remote_ssrc;
-}
-
-void ReceiveStatisticsProxy::UpdateHistograms(
- absl::optional<int> fraction_lost,
- const StreamDataCounters& rtp_stats,
- const StreamDataCounters* rtx_stats) {
- // Not actually running on the decoder thread, but must be called after
- // DecoderThreadStopped, which detaches the thread checker. It is therefore
- // safe to access `qp_counters_`, which were updated on the decode thread
- // earlier.
- RTC_DCHECK_RUN_ON(&decode_thread_);
-
- MutexLock lock(&mutex_);
-
- char log_stream_buf[8 * 1024];
- rtc::SimpleStringBuilder log_stream(log_stream_buf);
- int stream_duration_sec = (clock_->TimeInMilliseconds() - start_ms_) / 1000;
- if (stats_.frame_counts.key_frames > 0 ||
- stats_.frame_counts.delta_frames > 0) {
- RTC_HISTOGRAM_COUNTS_100000("WebRTC.Video.ReceiveStreamLifetimeInSeconds",
- stream_duration_sec);
- log_stream << "WebRTC.Video.ReceiveStreamLifetimeInSeconds "
- << stream_duration_sec << '\n';
- }
-
- log_stream << "Frames decoded " << stats_.frames_decoded << '\n';
-
- if (num_unique_frames_) {
- int num_dropped_frames = *num_unique_frames_ - stats_.frames_decoded;
- RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.DroppedFrames.Receiver",
- num_dropped_frames);
- log_stream << "WebRTC.Video.DroppedFrames.Receiver " << num_dropped_frames
- << '\n';
- }
-
- if (fraction_lost && stream_duration_sec >= metrics::kMinRunTimeInSeconds) {
- RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.ReceivedPacketsLostInPercent",
- *fraction_lost);
- log_stream << "WebRTC.Video.ReceivedPacketsLostInPercent " << *fraction_lost
- << '\n';
- }
-
- if (first_decoded_frame_time_ms_) {
- const int64_t elapsed_ms =
- (clock_->TimeInMilliseconds() - *first_decoded_frame_time_ms_);
- if (elapsed_ms >=
- metrics::kMinRunTimeInSeconds * rtc::kNumMillisecsPerSec) {
- int decoded_fps = static_cast<int>(
- (stats_.frames_decoded * 1000.0f / elapsed_ms) + 0.5f);
- RTC_HISTOGRAM_COUNTS_100("WebRTC.Video.DecodedFramesPerSecond",
- decoded_fps);
- log_stream << "WebRTC.Video.DecodedFramesPerSecond " << decoded_fps
- << '\n';
-
- const uint32_t frames_rendered = stats_.frames_rendered;
- if (frames_rendered > 0) {
- RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.DelayedFramesToRenderer",
- static_cast<int>(num_delayed_frames_rendered_ *
- 100 / frames_rendered));
- if (num_delayed_frames_rendered_ > 0) {
- RTC_HISTOGRAM_COUNTS_1000(
- "WebRTC.Video.DelayedFramesToRenderer_AvgDelayInMs",
- static_cast<int>(sum_missed_render_deadline_ms_ /
- num_delayed_frames_rendered_));
- }
- }
- }
- }
-
- const int kMinRequiredSamples = 200;
- int samples = static_cast<int>(render_fps_tracker_.TotalSampleCount());
- if (samples >= kMinRequiredSamples) {
- int rendered_fps = round(render_fps_tracker_.ComputeTotalRate());
- RTC_HISTOGRAM_COUNTS_100("WebRTC.Video.RenderFramesPerSecond",
- rendered_fps);
- log_stream << "WebRTC.Video.RenderFramesPerSecond " << rendered_fps << '\n';
- RTC_HISTOGRAM_COUNTS_100000(
- "WebRTC.Video.RenderSqrtPixelsPerSecond",
- round(render_pixel_tracker_.ComputeTotalRate()));
- }
-
- absl::optional<int> sync_offset_ms =
- sync_offset_counter_.Avg(kMinRequiredSamples);
- if (sync_offset_ms) {
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.AVSyncOffsetInMs",
- *sync_offset_ms);
- log_stream << "WebRTC.Video.AVSyncOffsetInMs " << *sync_offset_ms << '\n';
- }
- AggregatedStats freq_offset_stats = freq_offset_counter_.GetStats();
- if (freq_offset_stats.num_samples > 0) {
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.RtpToNtpFreqOffsetInKhz",
- freq_offset_stats.average);
- log_stream << "WebRTC.Video.RtpToNtpFreqOffsetInKhz "
- << freq_offset_stats.ToString() << '\n';
- }
-
- int num_total_frames =
- stats_.frame_counts.key_frames + stats_.frame_counts.delta_frames;
- if (num_total_frames >= kMinRequiredSamples) {
- int num_key_frames = stats_.frame_counts.key_frames;
- int key_frames_permille =
- (num_key_frames * 1000 + num_total_frames / 2) / num_total_frames;
- RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.KeyFramesReceivedInPermille",
- key_frames_permille);
- log_stream << "WebRTC.Video.KeyFramesReceivedInPermille "
- << key_frames_permille << '\n';
- }
-
- absl::optional<int> qp = qp_counters_.vp8.Avg(kMinRequiredSamples);
- if (qp) {
- RTC_HISTOGRAM_COUNTS_200("WebRTC.Video.Decoded.Vp8.Qp", *qp);
- log_stream << "WebRTC.Video.Decoded.Vp8.Qp " << *qp << '\n';
- }
- absl::optional<int> decode_ms = decode_time_counter_.Avg(kMinRequiredSamples);
- if (decode_ms) {
- RTC_HISTOGRAM_COUNTS_1000("WebRTC.Video.DecodeTimeInMs", *decode_ms);
- log_stream << "WebRTC.Video.DecodeTimeInMs " << *decode_ms << '\n';
- }
- absl::optional<int> jb_delay_ms =
- jitter_buffer_delay_counter_.Avg(kMinRequiredSamples);
- if (jb_delay_ms) {
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.JitterBufferDelayInMs",
- *jb_delay_ms);
- log_stream << "WebRTC.Video.JitterBufferDelayInMs " << *jb_delay_ms << '\n';
- }
-
- absl::optional<int> target_delay_ms =
- target_delay_counter_.Avg(kMinRequiredSamples);
- if (target_delay_ms) {
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.TargetDelayInMs",
- *target_delay_ms);
- log_stream << "WebRTC.Video.TargetDelayInMs " << *target_delay_ms << '\n';
- }
- absl::optional<int> current_delay_ms =
- current_delay_counter_.Avg(kMinRequiredSamples);
- if (current_delay_ms) {
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.CurrentDelayInMs",
- *current_delay_ms);
- log_stream << "WebRTC.Video.CurrentDelayInMs " << *current_delay_ms << '\n';
- }
- absl::optional<int> delay_ms = delay_counter_.Avg(kMinRequiredSamples);
- if (delay_ms)
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.OnewayDelayInMs", *delay_ms);
-
- // Aggregate content_specific_stats_ by removing experiment or simulcast
- // information;
- std::map<VideoContentType, ContentSpecificStats> aggregated_stats;
- for (const auto& it : content_specific_stats_) {
- // Calculate simulcast specific metrics (".S0" ... ".S2" suffixes).
- VideoContentType content_type = it.first;
- if (videocontenttypehelpers::GetSimulcastId(content_type) > 0) {
- // Aggregate on experiment id.
- videocontenttypehelpers::SetExperimentId(&content_type, 0);
- aggregated_stats[content_type].Add(it.second);
- }
- // Calculate experiment specific metrics (".ExperimentGroup[0-7]" suffixes).
- content_type = it.first;
- if (videocontenttypehelpers::GetExperimentId(content_type) > 0) {
- // Aggregate on simulcast id.
- videocontenttypehelpers::SetSimulcastId(&content_type, 0);
- aggregated_stats[content_type].Add(it.second);
- }
- // Calculate aggregated metrics (no suffixes. Aggregated on everything).
- content_type = it.first;
- videocontenttypehelpers::SetSimulcastId(&content_type, 0);
- videocontenttypehelpers::SetExperimentId(&content_type, 0);
- aggregated_stats[content_type].Add(it.second);
- }
-
- for (const auto& it : aggregated_stats) {
- // For the metric Foo we report the following slices:
- // WebRTC.Video.Foo,
- // WebRTC.Video.Screenshare.Foo,
- // WebRTC.Video.Foo.S[0-3],
- // WebRTC.Video.Foo.ExperimentGroup[0-7],
- // WebRTC.Video.Screenshare.Foo.S[0-3],
- // WebRTC.Video.Screenshare.Foo.ExperimentGroup[0-7].
- auto content_type = it.first;
- auto stats = it.second;
- std::string uma_prefix = UmaPrefixForContentType(content_type);
- std::string uma_suffix = UmaSuffixForContentType(content_type);
- // Metrics can be sliced on either simulcast id or experiment id but not
- // both.
- RTC_DCHECK(videocontenttypehelpers::GetExperimentId(content_type) == 0 ||
- videocontenttypehelpers::GetSimulcastId(content_type) == 0);
-
- absl::optional<int> e2e_delay_ms =
- stats.e2e_delay_counter.Avg(kMinRequiredSamples);
- if (e2e_delay_ms) {
- RTC_HISTOGRAM_COUNTS_SPARSE_10000(
- uma_prefix + ".EndToEndDelayInMs" + uma_suffix, *e2e_delay_ms);
- log_stream << uma_prefix << ".EndToEndDelayInMs" << uma_suffix << " "
- << *e2e_delay_ms << '\n';
- }
- absl::optional<int> e2e_delay_max_ms = stats.e2e_delay_counter.Max();
- if (e2e_delay_max_ms && e2e_delay_ms) {
- RTC_HISTOGRAM_COUNTS_SPARSE_100000(
- uma_prefix + ".EndToEndDelayMaxInMs" + uma_suffix, *e2e_delay_max_ms);
- log_stream << uma_prefix << ".EndToEndDelayMaxInMs" << uma_suffix << " "
- << *e2e_delay_max_ms << '\n';
- }
- absl::optional<int> interframe_delay_ms =
- stats.interframe_delay_counter.Avg(kMinRequiredSamples);
- if (interframe_delay_ms) {
- RTC_HISTOGRAM_COUNTS_SPARSE_10000(
- uma_prefix + ".InterframeDelayInMs" + uma_suffix,
- *interframe_delay_ms);
- log_stream << uma_prefix << ".InterframeDelayInMs" << uma_suffix << " "
- << *interframe_delay_ms << '\n';
- }
- absl::optional<int> interframe_delay_max_ms =
- stats.interframe_delay_counter.Max();
- if (interframe_delay_max_ms && interframe_delay_ms) {
- RTC_HISTOGRAM_COUNTS_SPARSE_10000(
- uma_prefix + ".InterframeDelayMaxInMs" + uma_suffix,
- *interframe_delay_max_ms);
- log_stream << uma_prefix << ".InterframeDelayMaxInMs" << uma_suffix << " "
- << *interframe_delay_max_ms << '\n';
- }
-
- absl::optional<uint32_t> interframe_delay_95p_ms =
- stats.interframe_delay_percentiles.GetPercentile(0.95f);
- if (interframe_delay_95p_ms && interframe_delay_ms != -1) {
- RTC_HISTOGRAM_COUNTS_SPARSE_10000(
- uma_prefix + ".InterframeDelay95PercentileInMs" + uma_suffix,
- *interframe_delay_95p_ms);
- log_stream << uma_prefix << ".InterframeDelay95PercentileInMs"
- << uma_suffix << " " << *interframe_delay_95p_ms << '\n';
- }
-
- absl::optional<int> width = stats.received_width.Avg(kMinRequiredSamples);
- if (width) {
- RTC_HISTOGRAM_COUNTS_SPARSE_10000(
- uma_prefix + ".ReceivedWidthInPixels" + uma_suffix, *width);
- log_stream << uma_prefix << ".ReceivedWidthInPixels" << uma_suffix << " "
- << *width << '\n';
- }
-
- absl::optional<int> height = stats.received_height.Avg(kMinRequiredSamples);
- if (height) {
- RTC_HISTOGRAM_COUNTS_SPARSE_10000(
- uma_prefix + ".ReceivedHeightInPixels" + uma_suffix, *height);
- log_stream << uma_prefix << ".ReceivedHeightInPixels" << uma_suffix << " "
- << *height << '\n';
- }
-
- if (content_type != VideoContentType::UNSPECIFIED) {
- // Don't report these 3 metrics unsliced, as more precise variants
- // are reported separately in this method.
- float flow_duration_sec = stats.flow_duration_ms / 1000.0;
- if (flow_duration_sec >= metrics::kMinRunTimeInSeconds) {
- int media_bitrate_kbps = static_cast<int>(stats.total_media_bytes * 8 /
- flow_duration_sec / 1000);
- RTC_HISTOGRAM_COUNTS_SPARSE_10000(
- uma_prefix + ".MediaBitrateReceivedInKbps" + uma_suffix,
- media_bitrate_kbps);
- log_stream << uma_prefix << ".MediaBitrateReceivedInKbps" << uma_suffix
- << " " << media_bitrate_kbps << '\n';
- }
-
- int num_total_frames2 =
- stats.frame_counts.key_frames + stats.frame_counts.delta_frames;
- if (num_total_frames2 >= kMinRequiredSamples) {
- int num_key_frames = stats.frame_counts.key_frames;
- int key_frames_permille =
- (num_key_frames * 1000 + num_total_frames2 / 2) / num_total_frames2;
- RTC_HISTOGRAM_COUNTS_SPARSE_1000(
- uma_prefix + ".KeyFramesReceivedInPermille" + uma_suffix,
- key_frames_permille);
- log_stream << uma_prefix << ".KeyFramesReceivedInPermille" << uma_suffix
- << " " << key_frames_permille << '\n';
- }
-
- absl::optional<int> qp2 = stats.qp_counter.Avg(kMinRequiredSamples);
- if (qp2) {
- RTC_HISTOGRAM_COUNTS_SPARSE_200(
- uma_prefix + ".Decoded.Vp8.Qp" + uma_suffix, *qp2);
- log_stream << uma_prefix << ".Decoded.Vp8.Qp" << uma_suffix << " "
- << *qp2 << '\n';
- }
- }
- }
-
- StreamDataCounters rtp_rtx_stats = rtp_stats;
- if (rtx_stats)
- rtp_rtx_stats.Add(*rtx_stats);
- int64_t elapsed_sec =
- rtp_rtx_stats.TimeSinceFirstPacketInMs(clock_->TimeInMilliseconds()) /
- 1000;
- if (elapsed_sec >= metrics::kMinRunTimeInSeconds) {
- RTC_HISTOGRAM_COUNTS_10000(
- "WebRTC.Video.BitrateReceivedInKbps",
- static_cast<int>(rtp_rtx_stats.transmitted.TotalBytes() * 8 /
- elapsed_sec / 1000));
- int media_bitrate_kbs = static_cast<int>(rtp_stats.MediaPayloadBytes() * 8 /
- elapsed_sec / 1000);
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.MediaBitrateReceivedInKbps",
- media_bitrate_kbs);
- log_stream << "WebRTC.Video.MediaBitrateReceivedInKbps "
- << media_bitrate_kbs << '\n';
- RTC_HISTOGRAM_COUNTS_10000(
- "WebRTC.Video.PaddingBitrateReceivedInKbps",
- static_cast<int>(rtp_rtx_stats.transmitted.padding_bytes * 8 /
- elapsed_sec / 1000));
- RTC_HISTOGRAM_COUNTS_10000(
- "WebRTC.Video.RetransmittedBitrateReceivedInKbps",
- static_cast<int>(rtp_rtx_stats.retransmitted.TotalBytes() * 8 /
- elapsed_sec / 1000));
- if (rtx_stats) {
- RTC_HISTOGRAM_COUNTS_10000(
- "WebRTC.Video.RtxBitrateReceivedInKbps",
- static_cast<int>(rtx_stats->transmitted.TotalBytes() * 8 /
- elapsed_sec / 1000));
- }
- const RtcpPacketTypeCounter& counters = stats_.rtcp_packet_type_counts;
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.NackPacketsSentPerMinute",
- counters.nack_packets * 60 / elapsed_sec);
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.FirPacketsSentPerMinute",
- counters.fir_packets * 60 / elapsed_sec);
- RTC_HISTOGRAM_COUNTS_10000("WebRTC.Video.PliPacketsSentPerMinute",
- counters.pli_packets * 60 / elapsed_sec);
- if (counters.nack_requests > 0) {
- RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.UniqueNackRequestsSentInPercent",
- counters.UniqueNackRequestsInPercent());
- }
- }
-
- if (num_certain_states_ >= kBadCallMinRequiredSamples) {
- RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.Any",
- 100 * num_bad_states_ / num_certain_states_);
- }
- absl::optional<double> fps_fraction =
- fps_threshold_.FractionHigh(kBadCallMinRequiredSamples);
- if (fps_fraction) {
- RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.FrameRate",
- static_cast<int>(100 * (1 - *fps_fraction)));
- }
- absl::optional<double> variance_fraction =
- variance_threshold_.FractionHigh(kBadCallMinRequiredSamples);
- if (variance_fraction) {
- RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.FrameRateVariance",
- static_cast<int>(100 * *variance_fraction));
- }
- absl::optional<double> qp_fraction =
- qp_threshold_.FractionHigh(kBadCallMinRequiredSamples);
- if (qp_fraction) {
- RTC_HISTOGRAM_PERCENTAGE("WebRTC.Video.BadCall.Qp",
- static_cast<int>(100 * *qp_fraction));
- }
-
- RTC_LOG(LS_INFO) << log_stream.str();
- video_quality_observer_->UpdateHistograms();
-}
-
-void ReceiveStatisticsProxy::QualitySample() {
- int64_t now = clock_->TimeInMilliseconds();
- if (last_sample_time_ + kMinSampleLengthMs > now)
- return;
-
- double fps =
- render_fps_tracker_.ComputeRateForInterval(now - last_sample_time_);
- absl::optional<int> qp = qp_sample_.Avg(1);
-
- bool prev_fps_bad = !fps_threshold_.IsHigh().value_or(true);
- bool prev_qp_bad = qp_threshold_.IsHigh().value_or(false);
- bool prev_variance_bad = variance_threshold_.IsHigh().value_or(false);
- bool prev_any_bad = prev_fps_bad || prev_qp_bad || prev_variance_bad;
-
- fps_threshold_.AddMeasurement(static_cast<int>(fps));
- if (qp)
- qp_threshold_.AddMeasurement(*qp);
- absl::optional<double> fps_variance_opt = fps_threshold_.CalculateVariance();
- double fps_variance = fps_variance_opt.value_or(0);
- if (fps_variance_opt) {
- variance_threshold_.AddMeasurement(static_cast<int>(fps_variance));
- }
-
- bool fps_bad = !fps_threshold_.IsHigh().value_or(true);
- bool qp_bad = qp_threshold_.IsHigh().value_or(false);
- bool variance_bad = variance_threshold_.IsHigh().value_or(false);
- bool any_bad = fps_bad || qp_bad || variance_bad;
-
- if (!prev_any_bad && any_bad) {
- RTC_LOG(LS_INFO) << "Bad call (any) start: " << now;
- } else if (prev_any_bad && !any_bad) {
- RTC_LOG(LS_INFO) << "Bad call (any) end: " << now;
- }
-
- if (!prev_fps_bad && fps_bad) {
- RTC_LOG(LS_INFO) << "Bad call (fps) start: " << now;
- } else if (prev_fps_bad && !fps_bad) {
- RTC_LOG(LS_INFO) << "Bad call (fps) end: " << now;
- }
-
- if (!prev_qp_bad && qp_bad) {
- RTC_LOG(LS_INFO) << "Bad call (qp) start: " << now;
- } else if (prev_qp_bad && !qp_bad) {
- RTC_LOG(LS_INFO) << "Bad call (qp) end: " << now;
- }
-
- if (!prev_variance_bad && variance_bad) {
- RTC_LOG(LS_INFO) << "Bad call (variance) start: " << now;
- } else if (prev_variance_bad && !variance_bad) {
- RTC_LOG(LS_INFO) << "Bad call (variance) end: " << now;
- }
-
- RTC_LOG(LS_VERBOSE) << "SAMPLE: sample_length: " << (now - last_sample_time_)
- << " fps: " << fps << " fps_bad: " << fps_bad
- << " qp: " << qp.value_or(-1) << " qp_bad: " << qp_bad
- << " variance_bad: " << variance_bad
- << " fps_variance: " << fps_variance;
-
- last_sample_time_ = now;
- qp_sample_.Reset();
-
- if (fps_threshold_.IsHigh() || variance_threshold_.IsHigh() ||
- qp_threshold_.IsHigh()) {
- if (any_bad)
- ++num_bad_states_;
- ++num_certain_states_;
- }
-}
-
-void ReceiveStatisticsProxy::UpdateFramerate(int64_t now_ms) const {
- int64_t old_frames_ms = now_ms - kRateStatisticsWindowSizeMs;
- while (!frame_window_.empty() &&
- frame_window_.begin()->first < old_frames_ms) {
- frame_window_.erase(frame_window_.begin());
- }
-
- size_t framerate =
- (frame_window_.size() * 1000 + 500) / kRateStatisticsWindowSizeMs;
- stats_.network_frame_rate = static_cast<int>(framerate);
-}
-
-void ReceiveStatisticsProxy::UpdateDecodeTimeHistograms(
- int width,
- int height,
- int decode_time_ms) const {
- bool is_4k = (width == 3840 || width == 4096) && height == 2160;
- bool is_hd = width == 1920 && height == 1080;
- // Only update histograms for 4k/HD and VP9/H264.
- if ((is_4k || is_hd) && (last_codec_type_ == kVideoCodecVP9 ||
- last_codec_type_ == kVideoCodecH264)) {
- const std::string kDecodeTimeUmaPrefix =
- "WebRTC.Video.DecodeTimePerFrameInMs.";
-
- // Each histogram needs its own line for it to not be reused in the wrong
- // way when the format changes.
- if (last_codec_type_ == kVideoCodecVP9) {
- bool is_sw_decoder =
- stats_.decoder_implementation_name.compare(0, 6, "libvpx") == 0;
- if (is_4k) {
- if (is_sw_decoder)
- RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "Vp9.4k.Sw",
- decode_time_ms);
- else
- RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "Vp9.4k.Hw",
- decode_time_ms);
- } else {
- if (is_sw_decoder)
- RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "Vp9.Hd.Sw",
- decode_time_ms);
- else
- RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "Vp9.Hd.Hw",
- decode_time_ms);
- }
- } else {
- bool is_sw_decoder =
- stats_.decoder_implementation_name.compare(0, 6, "FFmpeg") == 0;
- if (is_4k) {
- if (is_sw_decoder)
- RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "H264.4k.Sw",
- decode_time_ms);
- else
- RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "H264.4k.Hw",
- decode_time_ms);
-
- } else {
- if (is_sw_decoder)
- RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "H264.Hd.Sw",
- decode_time_ms);
- else
- RTC_HISTOGRAM_COUNTS_1000(kDecodeTimeUmaPrefix + "H264.Hd.Hw",
- decode_time_ms);
- }
- }
- }
-}
-
-absl::optional<int64_t>
-ReceiveStatisticsProxy::GetCurrentEstimatedPlayoutNtpTimestampMs(
- int64_t now_ms) const {
- if (!last_estimated_playout_ntp_timestamp_ms_ ||
- !last_estimated_playout_time_ms_) {
- return absl::nullopt;
- }
- int64_t elapsed_ms = now_ms - *last_estimated_playout_time_ms_;
- return *last_estimated_playout_ntp_timestamp_ms_ + elapsed_ms;
-}
-
-VideoReceiveStreamInterface::Stats ReceiveStatisticsProxy::GetStats() const {
- MutexLock lock(&mutex_);
- // Get current frame rates here, as only updating them on new frames prevents
- // us from ever correctly displaying frame rate of 0.
- int64_t now_ms = clock_->TimeInMilliseconds();
- UpdateFramerate(now_ms);
- stats_.render_frame_rate = renders_fps_estimator_.Rate(now_ms).value_or(0);
- stats_.decode_frame_rate = decode_fps_estimator_.Rate(now_ms).value_or(0);
- stats_.interframe_delay_max_ms =
- interframe_delay_max_moving_.Max(now_ms).value_or(-1);
- stats_.freeze_count = video_quality_observer_->NumFreezes();
- stats_.pause_count = video_quality_observer_->NumPauses();
- stats_.total_freezes_duration_ms =
- video_quality_observer_->TotalFreezesDurationMs();
- stats_.total_pauses_duration_ms =
- video_quality_observer_->TotalPausesDurationMs();
- stats_.total_frames_duration_ms =
- video_quality_observer_->TotalFramesDurationMs();
- stats_.sum_squared_frame_durations =
- video_quality_observer_->SumSquaredFrameDurationsSec();
- stats_.content_type = last_content_type_;
- stats_.timing_frame_info = timing_frame_info_counter_.Max(now_ms);
- stats_.jitter_buffer_delay_seconds =
- static_cast<double>(current_delay_counter_.Sum(1).value_or(0)) /
- rtc::kNumMillisecsPerSec;
- stats_.jitter_buffer_emitted_count = current_delay_counter_.NumSamples();
- stats_.estimated_playout_ntp_timestamp_ms =
- GetCurrentEstimatedPlayoutNtpTimestampMs(now_ms);
- return stats_;
-}
-
-void ReceiveStatisticsProxy::OnIncomingPayloadType(int payload_type) {
- MutexLock lock(&mutex_);
- stats_.current_payload_type = payload_type;
-}
-
-void ReceiveStatisticsProxy::OnDecoderImplementationName(
- const char* implementation_name) {
- MutexLock lock(&mutex_);
- stats_.decoder_implementation_name = implementation_name;
-}
-
-void ReceiveStatisticsProxy::OnFrameBufferTimingsUpdated(
- int max_decode_ms,
- int current_delay_ms,
- int target_delay_ms,
- int jitter_buffer_ms,
- int min_playout_delay_ms,
- int render_delay_ms) {
- MutexLock lock(&mutex_);
- stats_.max_decode_ms = max_decode_ms;
- stats_.current_delay_ms = current_delay_ms;
- stats_.target_delay_ms = target_delay_ms;
- stats_.jitter_buffer_ms = jitter_buffer_ms;
- stats_.min_playout_delay_ms = min_playout_delay_ms;
- stats_.render_delay_ms = render_delay_ms;
- jitter_buffer_delay_counter_.Add(jitter_buffer_ms);
- target_delay_counter_.Add(target_delay_ms);
- current_delay_counter_.Add(current_delay_ms);
- // Network delay (rtt/2) + target_delay_ms (jitter delay + decode time +
- // render delay).
- delay_counter_.Add(target_delay_ms + avg_rtt_ms_ / 2);
-}
-
-void ReceiveStatisticsProxy::OnUniqueFramesCounted(int num_unique_frames) {
- MutexLock lock(&mutex_);
- num_unique_frames_.emplace(num_unique_frames);
-}
-
-void ReceiveStatisticsProxy::OnTimingFrameInfoUpdated(
- const TimingFrameInfo& info) {
- MutexLock lock(&mutex_);
- if (info.flags != VideoSendTiming::kInvalid) {
- int64_t now_ms = clock_->TimeInMilliseconds();
- timing_frame_info_counter_.Add(info, now_ms);
- }
-
- // Measure initial decoding latency between the first frame arriving and the
- // first frame being decoded.
- if (!first_frame_received_time_ms_.has_value()) {
- first_frame_received_time_ms_ = info.receive_finish_ms;
- }
- if (stats_.first_frame_received_to_decoded_ms == -1 &&
- first_decoded_frame_time_ms_) {
- stats_.first_frame_received_to_decoded_ms =
- *first_decoded_frame_time_ms_ - *first_frame_received_time_ms_;
- }
-}
-
-void ReceiveStatisticsProxy::RtcpPacketTypesCounterUpdated(
- uint32_t ssrc,
- const RtcpPacketTypeCounter& packet_counter) {
- MutexLock lock(&mutex_);
- if (stats_.ssrc != ssrc)
- return;
- stats_.rtcp_packet_type_counts = packet_counter;
-}
-
-void ReceiveStatisticsProxy::OnCname(uint32_t ssrc, absl::string_view cname) {
- MutexLock lock(&mutex_);
- // TODO(pbos): Handle both local and remote ssrcs here and RTC_DCHECK that we
- // receive stats from one of them.
- if (stats_.ssrc != ssrc)
- return;
- stats_.c_name = std::string(cname);
-}
-
-void ReceiveStatisticsProxy::OnDecodedFrame(const VideoFrame& frame,
- absl::optional<uint8_t> qp,
- int32_t decode_time_ms,
- VideoContentType content_type) {
- MutexLock lock(&mutex_);
-
- uint64_t now_ms = clock_->TimeInMilliseconds();
-
- if (videocontenttypehelpers::IsScreenshare(content_type) !=
- videocontenttypehelpers::IsScreenshare(last_content_type_)) {
- // Reset the quality observer if content type is switched. But first report
- // stats for the previous part of the call.
- video_quality_observer_->UpdateHistograms();
- video_quality_observer_.reset(new VideoQualityObserver(content_type));
- }
-
- video_quality_observer_->OnDecodedFrame(frame, qp, last_codec_type_);
-
- ContentSpecificStats* content_specific_stats =
- &content_specific_stats_[content_type];
- ++stats_.frames_decoded;
- if (qp) {
- if (!stats_.qp_sum) {
- if (stats_.frames_decoded != 1) {
- RTC_LOG(LS_WARNING)
- << "Frames decoded was not 1 when first qp value was received.";
- }
- stats_.qp_sum = 0;
- }
- *stats_.qp_sum += *qp;
- content_specific_stats->qp_counter.Add(*qp);
- } else if (stats_.qp_sum) {
- RTC_LOG(LS_WARNING)
- << "QP sum was already set and no QP was given for a frame.";
- stats_.qp_sum.reset();
- }
- decode_time_counter_.Add(decode_time_ms);
- stats_.decode_ms = decode_time_ms;
- stats_.total_decode_time_ms += decode_time_ms;
- if (enable_decode_time_histograms_) {
- UpdateDecodeTimeHistograms(frame.width(), frame.height(), decode_time_ms);
- }
-
- last_content_type_ = content_type;
- decode_fps_estimator_.Update(1, now_ms);
- if (last_decoded_frame_time_ms_) {
- int64_t interframe_delay_ms = now_ms - *last_decoded_frame_time_ms_;
- RTC_DCHECK_GE(interframe_delay_ms, 0);
- double interframe_delay = interframe_delay_ms / 1000.0;
- stats_.total_inter_frame_delay += interframe_delay;
- stats_.total_squared_inter_frame_delay +=
- interframe_delay * interframe_delay;
- interframe_delay_max_moving_.Add(interframe_delay_ms, now_ms);
- content_specific_stats->interframe_delay_counter.Add(interframe_delay_ms);
- content_specific_stats->interframe_delay_percentiles.Add(
- interframe_delay_ms);
- content_specific_stats->flow_duration_ms += interframe_delay_ms;
- }
- if (stats_.frames_decoded == 1) {
- first_decoded_frame_time_ms_.emplace(now_ms);
- }
- last_decoded_frame_time_ms_.emplace(now_ms);
-}
-
-void ReceiveStatisticsProxy::OnRenderedFrame(const VideoFrame& frame) {
- int width = frame.width();
- int height = frame.height();
- RTC_DCHECK_GT(width, 0);
- RTC_DCHECK_GT(height, 0);
- int64_t now_ms = clock_->TimeInMilliseconds();
- MutexLock lock(&mutex_);
-
- video_quality_observer_->OnRenderedFrame(frame, now_ms);
-
- ContentSpecificStats* content_specific_stats =
- &content_specific_stats_[last_content_type_];
- renders_fps_estimator_.Update(1, now_ms);
- ++stats_.frames_rendered;
- stats_.width = width;
- stats_.height = height;
- render_fps_tracker_.AddSamples(1);
- render_pixel_tracker_.AddSamples(sqrt(width * height));
- content_specific_stats->received_width.Add(width);
- content_specific_stats->received_height.Add(height);
-
- // Consider taking stats_.render_delay_ms into account.
- const int64_t time_until_rendering_ms = frame.render_time_ms() - now_ms;
- if (time_until_rendering_ms < 0) {
- sum_missed_render_deadline_ms_ += -time_until_rendering_ms;
- ++num_delayed_frames_rendered_;
- }
-
- if (frame.ntp_time_ms() > 0) {
- int64_t delay_ms = clock_->CurrentNtpInMilliseconds() - frame.ntp_time_ms();
- if (delay_ms >= 0) {
- content_specific_stats->e2e_delay_counter.Add(delay_ms);
- }
- }
- QualitySample();
-}
-
-void ReceiveStatisticsProxy::OnSyncOffsetUpdated(int64_t video_playout_ntp_ms,
- int64_t sync_offset_ms,
- double estimated_freq_khz) {
- MutexLock lock(&mutex_);
- sync_offset_counter_.Add(std::abs(sync_offset_ms));
- stats_.sync_offset_ms = sync_offset_ms;
- last_estimated_playout_ntp_timestamp_ms_ = video_playout_ntp_ms;
- last_estimated_playout_time_ms_ = clock_->TimeInMilliseconds();
-
- const double kMaxFreqKhz = 10000.0;
- int offset_khz = kMaxFreqKhz;
- // Should not be zero or negative. If so, report max.
- if (estimated_freq_khz < kMaxFreqKhz && estimated_freq_khz > 0.0)
- offset_khz = static_cast<int>(std::fabs(estimated_freq_khz - 90.0) + 0.5);
-
- freq_offset_counter_.Add(offset_khz);
-}
-
-void ReceiveStatisticsProxy::OnCompleteFrame(bool is_keyframe,
- size_t size_bytes,
- VideoContentType content_type) {
- MutexLock lock(&mutex_);
- if (is_keyframe) {
- ++stats_.frame_counts.key_frames;
- } else {
- ++stats_.frame_counts.delta_frames;
- }
-
- // Content type extension is set only for keyframes and should be propagated
- // for all the following delta frames. Here we may receive frames out of order
- // and miscategorise some delta frames near the layer switch.
- // This may slightly offset calculated bitrate and keyframes permille metrics.
- VideoContentType propagated_content_type =
- is_keyframe ? content_type : last_content_type_;
-
- ContentSpecificStats* content_specific_stats =
- &content_specific_stats_[propagated_content_type];
-
- content_specific_stats->total_media_bytes += size_bytes;
- if (is_keyframe) {
- ++content_specific_stats->frame_counts.key_frames;
- } else {
- ++content_specific_stats->frame_counts.delta_frames;
- }
-
- int64_t now_ms = clock_->TimeInMilliseconds();
- frame_window_.insert(std::make_pair(now_ms, size_bytes));
- UpdateFramerate(now_ms);
-}
-
-void ReceiveStatisticsProxy::OnDroppedFrames(uint32_t frames_dropped) {
- MutexLock lock(&mutex_);
- stats_.frames_dropped += frames_dropped;
-}
-
-void ReceiveStatisticsProxy::OnPreDecode(VideoCodecType codec_type, int qp) {
- RTC_DCHECK_RUN_ON(&decode_thread_);
- MutexLock lock(&mutex_);
- last_codec_type_ = codec_type;
- if (last_codec_type_ == kVideoCodecVP8 && qp != -1) {
- qp_counters_.vp8.Add(qp);
- qp_sample_.Add(qp);
- }
-}
-
-void ReceiveStatisticsProxy::OnStreamInactive() {
- // TODO(sprang): Figure out any other state that should be reset.
-
- MutexLock lock(&mutex_);
- // Don't report inter-frame delay if stream was paused.
- last_decoded_frame_time_ms_.reset();
- video_quality_observer_->OnStreamInactive();
-}
-
-void ReceiveStatisticsProxy::OnRttUpdate(int64_t avg_rtt_ms,
- int64_t max_rtt_ms) {
- MutexLock lock(&mutex_);
- avg_rtt_ms_ = avg_rtt_ms;
-}
-
-void ReceiveStatisticsProxy::DecoderThreadStarting() {
- RTC_DCHECK_RUN_ON(&main_thread_);
-}
-
-void ReceiveStatisticsProxy::DecoderThreadStopped() {
- RTC_DCHECK_RUN_ON(&main_thread_);
- decode_thread_.Detach();
-}
-
-ReceiveStatisticsProxy::ContentSpecificStats::ContentSpecificStats()
- : interframe_delay_percentiles(kMaxCommonInterframeDelayMs) {}
-
-ReceiveStatisticsProxy::ContentSpecificStats::~ContentSpecificStats() = default;
-
-void ReceiveStatisticsProxy::ContentSpecificStats::Add(
- const ContentSpecificStats& other) {
- e2e_delay_counter.Add(other.e2e_delay_counter);
- interframe_delay_counter.Add(other.interframe_delay_counter);
- flow_duration_ms += other.flow_duration_ms;
- total_media_bytes += other.total_media_bytes;
- received_height.Add(other.received_height);
- received_width.Add(other.received_width);
- qp_counter.Add(other.qp_counter);
- frame_counts.key_frames += other.frame_counts.key_frames;
- frame_counts.delta_frames += other.frame_counts.delta_frames;
- interframe_delay_percentiles.Add(other.interframe_delay_percentiles);
-}
-} // namespace webrtc
diff --git a/video/receive_statistics_proxy.h b/video/receive_statistics_proxy.h
deleted file mode 100644
index f0b5148..0000000
--- a/video/receive_statistics_proxy.h
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef VIDEO_RECEIVE_STATISTICS_PROXY_H_
-#define VIDEO_RECEIVE_STATISTICS_PROXY_H_
-
-#include <map>
-#include <memory>
-#include <string>
-#include <vector>
-
-#include "absl/types/optional.h"
-#include "api/field_trials_view.h"
-#include "api/sequence_checker.h"
-#include "call/video_receive_stream.h"
-#include "modules/include/module_common_types.h"
-#include "modules/video_coding/include/video_coding_defines.h"
-#include "rtc_base/numerics/histogram_percentile_counter.h"
-#include "rtc_base/numerics/moving_max_counter.h"
-#include "rtc_base/numerics/sample_counter.h"
-#include "rtc_base/rate_statistics.h"
-#include "rtc_base/rate_tracker.h"
-#include "rtc_base/synchronization/mutex.h"
-#include "rtc_base/thread_annotations.h"
-#include "video/quality_threshold.h"
-#include "video/stats_counter.h"
-#include "video/video_quality_observer.h"
-
-namespace webrtc {
-
-class Clock;
-struct CodecSpecificInfo;
-
-class ReceiveStatisticsProxy : public VCMReceiveStatisticsCallback,
- public RtcpCnameCallback,
- public RtcpPacketTypeCounterObserver,
- public CallStatsObserver {
- public:
- ReceiveStatisticsProxy(uint32_t remote_ssrc,
- Clock* clock,
- const FieldTrialsView* field_trials = nullptr);
- ~ReceiveStatisticsProxy() = default;
-
- VideoReceiveStreamInterface::Stats GetStats() const;
-
- void OnDecodedFrame(const VideoFrame& frame,
- absl::optional<uint8_t> qp,
- int32_t decode_time_ms,
- VideoContentType content_type);
- void OnSyncOffsetUpdated(int64_t video_playout_ntp_ms,
- int64_t sync_offset_ms,
- double estimated_freq_khz);
- void OnRenderedFrame(const VideoFrame& frame);
- void OnIncomingPayloadType(int payload_type);
- void OnDecoderImplementationName(const char* implementation_name);
-
- void OnPreDecode(VideoCodecType codec_type, int qp);
-
- void OnUniqueFramesCounted(int num_unique_frames);
-
- // Indicates video stream has been paused (no incoming packets).
- void OnStreamInactive();
-
- // Overrides VCMReceiveStatisticsCallback.
- void OnCompleteFrame(bool is_keyframe,
- size_t size_bytes,
- VideoContentType content_type) override;
- void OnDroppedFrames(uint32_t frames_dropped) override;
- void OnFrameBufferTimingsUpdated(int max_decode_ms,
- int current_delay_ms,
- int target_delay_ms,
- int jitter_buffer_ms,
- int min_playout_delay_ms,
- int render_delay_ms) override;
-
- void OnTimingFrameInfoUpdated(const TimingFrameInfo& info) override;
-
- // Overrides RtcpCnameCallback.
- void OnCname(uint32_t ssrc, absl::string_view cname) override;
-
- // Overrides RtcpPacketTypeCounterObserver.
- void RtcpPacketTypesCounterUpdated(
- uint32_t ssrc,
- const RtcpPacketTypeCounter& packet_counter) override;
-
- // Implements CallStatsObserver.
- void OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) override;
-
- // Notification methods that are used to check our internal state and validate
- // threading assumptions. These are called by VideoReceiveStreamInterface.
- void DecoderThreadStarting();
- void DecoderThreadStopped();
-
- // Produce histograms. Must be called after DecoderThreadStopped(), typically
- // at the end of the call.
- void UpdateHistograms(absl::optional<int> fraction_lost,
- const StreamDataCounters& rtp_stats,
- const StreamDataCounters* rtx_stats);
-
- private:
- struct QpCounters {
- rtc::SampleCounter vp8;
- };
-
- struct ContentSpecificStats {
- ContentSpecificStats();
- ~ContentSpecificStats();
-
- void Add(const ContentSpecificStats& other);
-
- rtc::SampleCounter e2e_delay_counter;
- rtc::SampleCounter interframe_delay_counter;
- int64_t flow_duration_ms = 0;
- int64_t total_media_bytes = 0;
- rtc::SampleCounter received_width;
- rtc::SampleCounter received_height;
- rtc::SampleCounter qp_counter;
- FrameCounts frame_counts;
- rtc::HistogramPercentileCounter interframe_delay_percentiles;
- };
-
- void QualitySample() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
-
- // Removes info about old frames and then updates the framerate.
- void UpdateFramerate(int64_t now_ms) const
- RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
-
- void UpdateDecodeTimeHistograms(int width,
- int height,
- int decode_time_ms) const
- RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
-
- absl::optional<int64_t> GetCurrentEstimatedPlayoutNtpTimestampMs(
- int64_t now_ms) const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
-
- Clock* const clock_;
- const int64_t start_ms_;
- const bool enable_decode_time_histograms_;
-
- mutable Mutex mutex_;
- int64_t last_sample_time_ RTC_GUARDED_BY(mutex_);
- QualityThreshold fps_threshold_ RTC_GUARDED_BY(mutex_);
- QualityThreshold qp_threshold_ RTC_GUARDED_BY(mutex_);
- QualityThreshold variance_threshold_ RTC_GUARDED_BY(mutex_);
- rtc::SampleCounter qp_sample_ RTC_GUARDED_BY(mutex_);
- int num_bad_states_ RTC_GUARDED_BY(mutex_);
- int num_certain_states_ RTC_GUARDED_BY(mutex_);
- // Note: The `stats_.rtp_stats` member is not used or populated by this class.
- mutable VideoReceiveStreamInterface::Stats stats_ RTC_GUARDED_BY(mutex_);
- RateStatistics decode_fps_estimator_ RTC_GUARDED_BY(mutex_);
- RateStatistics renders_fps_estimator_ RTC_GUARDED_BY(mutex_);
- rtc::RateTracker render_fps_tracker_ RTC_GUARDED_BY(mutex_);
- rtc::RateTracker render_pixel_tracker_ RTC_GUARDED_BY(mutex_);
- rtc::SampleCounter sync_offset_counter_ RTC_GUARDED_BY(mutex_);
- rtc::SampleCounter decode_time_counter_ RTC_GUARDED_BY(mutex_);
- rtc::SampleCounter jitter_buffer_delay_counter_ RTC_GUARDED_BY(mutex_);
- rtc::SampleCounter target_delay_counter_ RTC_GUARDED_BY(mutex_);
- rtc::SampleCounter current_delay_counter_ RTC_GUARDED_BY(mutex_);
- rtc::SampleCounter delay_counter_ RTC_GUARDED_BY(mutex_);
- std::unique_ptr<VideoQualityObserver> video_quality_observer_
- RTC_GUARDED_BY(mutex_);
- mutable rtc::MovingMaxCounter<int> interframe_delay_max_moving_
- RTC_GUARDED_BY(mutex_);
- std::map<VideoContentType, ContentSpecificStats> content_specific_stats_
- RTC_GUARDED_BY(mutex_);
- MaxCounter freq_offset_counter_ RTC_GUARDED_BY(mutex_);
- QpCounters qp_counters_ RTC_GUARDED_BY(decode_thread_);
- int64_t avg_rtt_ms_ RTC_GUARDED_BY(mutex_);
- mutable std::map<int64_t, size_t> frame_window_ RTC_GUARDED_BY(&mutex_);
- VideoContentType last_content_type_ RTC_GUARDED_BY(&mutex_);
- VideoCodecType last_codec_type_ RTC_GUARDED_BY(&mutex_);
- absl::optional<int64_t> first_frame_received_time_ms_ RTC_GUARDED_BY(&mutex_);
- absl::optional<int64_t> first_decoded_frame_time_ms_ RTC_GUARDED_BY(&mutex_);
- absl::optional<int64_t> last_decoded_frame_time_ms_ RTC_GUARDED_BY(&mutex_);
- size_t num_delayed_frames_rendered_ RTC_GUARDED_BY(&mutex_);
- int64_t sum_missed_render_deadline_ms_ RTC_GUARDED_BY(&mutex_);
- // Mutable because calling Max() on MovingMaxCounter is not const. Yet it is
- // called from const GetStats().
- mutable rtc::MovingMaxCounter<TimingFrameInfo> timing_frame_info_counter_
- RTC_GUARDED_BY(&mutex_);
- absl::optional<int> num_unique_frames_ RTC_GUARDED_BY(mutex_);
- absl::optional<int64_t> last_estimated_playout_ntp_timestamp_ms_
- RTC_GUARDED_BY(&mutex_);
- absl::optional<int64_t> last_estimated_playout_time_ms_
- RTC_GUARDED_BY(&mutex_);
- SequenceChecker decode_thread_;
- SequenceChecker network_thread_;
- SequenceChecker main_thread_;
-};
-
-} // namespace webrtc
-#endif // VIDEO_RECEIVE_STATISTICS_PROXY_H_
diff --git a/video/receive_statistics_proxy_unittest.cc b/video/receive_statistics_proxy_unittest.cc
deleted file mode 100644
index 1bef646..0000000
--- a/video/receive_statistics_proxy_unittest.cc
+++ /dev/null
@@ -1,1827 +0,0 @@
-/*
- * Copyright 2016 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/receive_statistics_proxy.h"
-
-#include <limits>
-#include <memory>
-#include <string>
-#include <tuple>
-#include <utility>
-
-#include "absl/types/optional.h"
-#include "api/scoped_refptr.h"
-#include "api/video/i420_buffer.h"
-#include "api/video/video_frame.h"
-#include "api/video/video_frame_buffer.h"
-#include "api/video/video_rotation.h"
-#include "system_wrappers/include/metrics.h"
-#include "test/gtest.h"
-#include "test/scoped_key_value_config.h"
-
-namespace webrtc {
-namespace {
-const int64_t kFreqOffsetProcessIntervalInMs = 40000;
-const uint32_t kLocalSsrc = 123;
-const uint32_t kRemoteSsrc = 456;
-const int kMinRequiredSamples = 200;
-const int kWidth = 1280;
-const int kHeight = 720;
-} // namespace
-
-// TODO(sakal): ReceiveStatisticsProxy is lacking unittesting.
-class ReceiveStatisticsProxyTest : public ::testing::Test {
- public:
- explicit ReceiveStatisticsProxyTest(std::string field_trials = "")
- : field_trials_(field_trials),
- fake_clock_(1234),
- config_(GetTestConfig()) {}
- virtual ~ReceiveStatisticsProxyTest() {}
-
- protected:
- virtual void SetUp() {
- metrics::Reset();
- statistics_proxy_.reset(new ReceiveStatisticsProxy(
- config_.rtp.remote_ssrc, &fake_clock_, &field_trials_));
- }
-
- VideoReceiveStreamInterface::Config GetTestConfig() {
- VideoReceiveStreamInterface::Config config(nullptr);
- config.rtp.local_ssrc = kLocalSsrc;
- config.rtp.remote_ssrc = kRemoteSsrc;
- return config;
- }
-
- VideoFrame CreateFrame(int width, int height) {
- return CreateVideoFrame(width, height, 0);
- }
-
- VideoFrame CreateFrameWithRenderTimeMs(int64_t render_time_ms) {
- return CreateVideoFrame(kWidth, kHeight, render_time_ms);
- }
-
- VideoFrame CreateVideoFrame(int width, int height, int64_t render_time_ms) {
- VideoFrame frame =
- VideoFrame::Builder()
- .set_video_frame_buffer(I420Buffer::Create(width, height))
- .set_timestamp_rtp(0)
- .set_timestamp_ms(render_time_ms)
- .set_rotation(kVideoRotation_0)
- .build();
- frame.set_ntp_time_ms(fake_clock_.CurrentNtpInMilliseconds());
- return frame;
- }
-
- test::ScopedKeyValueConfig field_trials_;
- SimulatedClock fake_clock_;
- const VideoReceiveStreamInterface::Config config_;
- std::unique_ptr<ReceiveStatisticsProxy> statistics_proxy_;
-};
-
-TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameIncreasesFramesDecoded) {
- EXPECT_EQ(0u, statistics_proxy_->GetStats().frames_decoded);
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- for (uint32_t i = 1; i <= 3; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(i, statistics_proxy_->GetStats().frames_decoded);
- }
-}
-
-TEST_F(ReceiveStatisticsProxyTest, DecodedFpsIsReported) {
- const int kFps = 20;
- const int kRequiredSamples = metrics::kMinRunTimeInSeconds * kFps;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- for (int i = 0; i < kRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- fake_clock_.AdvanceTimeMilliseconds(1000 / kFps);
- }
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.DecodedFramesPerSecond"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.DecodedFramesPerSecond", kFps));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, DecodedFpsIsNotReportedForTooFewSamples) {
- const int kFps = 20;
- const int kRequiredSamples = metrics::kMinRunTimeInSeconds * kFps;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- for (int i = 0; i < kRequiredSamples - 1; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- fake_clock_.AdvanceTimeMilliseconds(1000 / kFps);
- }
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(0,
- metrics::NumSamples("WebRTC.Video.DecodedFramesPerSecond"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest,
- OnDecodedFrameWithQpDoesNotResetFramesDecodedOrTotalDecodeTime) {
- EXPECT_EQ(0u, statistics_proxy_->GetStats().frames_decoded);
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- unsigned int expected_total_decode_time_ms = 0;
- unsigned int expected_frames_decoded = 0;
- for (uint32_t i = 1; i <= 3; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 1,
- VideoContentType::UNSPECIFIED);
- expected_total_decode_time_ms += 1;
- ++expected_frames_decoded;
- EXPECT_EQ(expected_frames_decoded,
- statistics_proxy_->GetStats().frames_decoded);
- EXPECT_EQ(expected_total_decode_time_ms,
- statistics_proxy_->GetStats().total_decode_time_ms);
- }
- statistics_proxy_->OnDecodedFrame(frame, 1u, 3,
- VideoContentType::UNSPECIFIED);
- ++expected_frames_decoded;
- expected_total_decode_time_ms += 3;
- EXPECT_EQ(expected_frames_decoded,
- statistics_proxy_->GetStats().frames_decoded);
- EXPECT_EQ(expected_total_decode_time_ms,
- statistics_proxy_->GetStats().total_decode_time_ms);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameIncreasesQpSum) {
- EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- statistics_proxy_->OnDecodedFrame(frame, 3u, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(3u, statistics_proxy_->GetStats().qp_sum);
- statistics_proxy_->OnDecodedFrame(frame, 127u, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(130u, statistics_proxy_->GetStats().qp_sum);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameIncreasesTotalDecodeTime) {
- EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- statistics_proxy_->OnDecodedFrame(frame, 3u, 4,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(4u, statistics_proxy_->GetStats().total_decode_time_ms);
- statistics_proxy_->OnDecodedFrame(frame, 127u, 7,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(11u, statistics_proxy_->GetStats().total_decode_time_ms);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, ReportsContentType) {
- const std::string kRealtimeString("realtime");
- const std::string kScreenshareString("screen");
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- EXPECT_EQ(kRealtimeString, videocontenttypehelpers::ToString(
- statistics_proxy_->GetStats().content_type));
- statistics_proxy_->OnDecodedFrame(frame, 3u, 0,
- VideoContentType::SCREENSHARE);
- EXPECT_EQ(kScreenshareString,
- videocontenttypehelpers::ToString(
- statistics_proxy_->GetStats().content_type));
- statistics_proxy_->OnDecodedFrame(frame, 3u, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(kRealtimeString, videocontenttypehelpers::ToString(
- statistics_proxy_->GetStats().content_type));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, ReportsMaxTotalInterFrameDelay) {
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- const TimeDelta kInterFrameDelay1 = TimeDelta::Millis(100);
- const TimeDelta kInterFrameDelay2 = TimeDelta::Millis(200);
- const TimeDelta kInterFrameDelay3 = TimeDelta::Millis(300);
- double expected_total_inter_frame_delay = 0;
- double expected_total_squared_inter_frame_delay = 0;
- EXPECT_EQ(expected_total_inter_frame_delay,
- statistics_proxy_->GetStats().total_inter_frame_delay);
- EXPECT_EQ(expected_total_squared_inter_frame_delay,
- statistics_proxy_->GetStats().total_squared_inter_frame_delay);
-
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_DOUBLE_EQ(expected_total_inter_frame_delay,
- statistics_proxy_->GetStats().total_inter_frame_delay);
- EXPECT_DOUBLE_EQ(
- expected_total_squared_inter_frame_delay,
- statistics_proxy_->GetStats().total_squared_inter_frame_delay);
-
- fake_clock_.AdvanceTime(kInterFrameDelay1);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- expected_total_inter_frame_delay += kInterFrameDelay1.seconds<double>();
- expected_total_squared_inter_frame_delay +=
- pow(kInterFrameDelay1.seconds<double>(), 2.0);
- EXPECT_DOUBLE_EQ(expected_total_inter_frame_delay,
- statistics_proxy_->GetStats().total_inter_frame_delay);
- EXPECT_DOUBLE_EQ(
- expected_total_squared_inter_frame_delay,
- statistics_proxy_->GetStats().total_squared_inter_frame_delay);
-
- fake_clock_.AdvanceTime(kInterFrameDelay2);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- expected_total_inter_frame_delay += kInterFrameDelay2.seconds<double>();
- expected_total_squared_inter_frame_delay +=
- pow(kInterFrameDelay2.seconds<double>(), 2.0);
- EXPECT_DOUBLE_EQ(expected_total_inter_frame_delay,
- statistics_proxy_->GetStats().total_inter_frame_delay);
- EXPECT_DOUBLE_EQ(
- expected_total_squared_inter_frame_delay,
- statistics_proxy_->GetStats().total_squared_inter_frame_delay);
-
- fake_clock_.AdvanceTime(kInterFrameDelay3);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- expected_total_inter_frame_delay += kInterFrameDelay3.seconds<double>();
- expected_total_squared_inter_frame_delay +=
- pow(kInterFrameDelay3.seconds<double>(), 2.0);
- EXPECT_DOUBLE_EQ(expected_total_inter_frame_delay,
- statistics_proxy_->GetStats().total_inter_frame_delay);
- EXPECT_DOUBLE_EQ(
- expected_total_squared_inter_frame_delay,
- statistics_proxy_->GetStats().total_squared_inter_frame_delay);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, ReportsMaxInterframeDelay) {
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- const int64_t kInterframeDelayMs1 = 100;
- const int64_t kInterframeDelayMs2 = 200;
- const int64_t kInterframeDelayMs3 = 100;
- EXPECT_EQ(-1, statistics_proxy_->GetStats().interframe_delay_max_ms);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(-1, statistics_proxy_->GetStats().interframe_delay_max_ms);
-
- fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs1);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(kInterframeDelayMs1,
- statistics_proxy_->GetStats().interframe_delay_max_ms);
-
- fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs2);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(kInterframeDelayMs2,
- statistics_proxy_->GetStats().interframe_delay_max_ms);
-
- fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs3);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- // kInterframeDelayMs3 is smaller than kInterframeDelayMs2.
- EXPECT_EQ(kInterframeDelayMs2,
- statistics_proxy_->GetStats().interframe_delay_max_ms);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, ReportInterframeDelayInWindow) {
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- const int64_t kInterframeDelayMs1 = 900;
- const int64_t kInterframeDelayMs2 = 750;
- const int64_t kInterframeDelayMs3 = 700;
- EXPECT_EQ(-1, statistics_proxy_->GetStats().interframe_delay_max_ms);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(-1, statistics_proxy_->GetStats().interframe_delay_max_ms);
-
- fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs1);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(kInterframeDelayMs1,
- statistics_proxy_->GetStats().interframe_delay_max_ms);
-
- fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs2);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- // Still first delay is the maximum
- EXPECT_EQ(kInterframeDelayMs1,
- statistics_proxy_->GetStats().interframe_delay_max_ms);
-
- fake_clock_.AdvanceTimeMilliseconds(kInterframeDelayMs3);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- // Now the first sample is out of the window, so the second is the maximum.
- EXPECT_EQ(kInterframeDelayMs2,
- statistics_proxy_->GetStats().interframe_delay_max_ms);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, ReportsFreezeMetrics) {
- const int64_t kFreezeDurationMs = 1000;
-
- VideoReceiveStreamInterface::Stats stats = statistics_proxy_->GetStats();
- EXPECT_EQ(0u, stats.freeze_count);
- EXPECT_FALSE(stats.total_freezes_duration_ms);
-
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- for (size_t i = 0; i < VideoQualityObserver::kMinFrameSamplesToDetectFreeze;
- ++i) {
- fake_clock_.AdvanceTimeMilliseconds(30);
- statistics_proxy_->OnRenderedFrame(frame);
- }
-
- // Freeze.
- fake_clock_.AdvanceTimeMilliseconds(kFreezeDurationMs);
- statistics_proxy_->OnRenderedFrame(frame);
-
- stats = statistics_proxy_->GetStats();
- EXPECT_EQ(1u, stats.freeze_count);
- EXPECT_EQ(kFreezeDurationMs, stats.total_freezes_duration_ms);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, ReportsPauseMetrics) {
- VideoReceiveStreamInterface::Stats stats = statistics_proxy_->GetStats();
- ASSERT_EQ(0u, stats.pause_count);
- ASSERT_EQ(0u, stats.total_pauses_duration_ms);
-
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- statistics_proxy_->OnRenderedFrame(frame);
-
- // Pause.
- fake_clock_.AdvanceTimeMilliseconds(5432);
- statistics_proxy_->OnStreamInactive();
- statistics_proxy_->OnRenderedFrame(frame);
-
- stats = statistics_proxy_->GetStats();
- EXPECT_EQ(1u, stats.pause_count);
- EXPECT_EQ(5432u, stats.total_pauses_duration_ms);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, PauseBeforeFirstAndAfterLastFrameIgnored) {
- VideoReceiveStreamInterface::Stats stats = statistics_proxy_->GetStats();
- ASSERT_EQ(0u, stats.pause_count);
- ASSERT_EQ(0u, stats.total_pauses_duration_ms);
-
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- // Pause -> Frame -> Pause
- fake_clock_.AdvanceTimeMilliseconds(5000);
- statistics_proxy_->OnStreamInactive();
- statistics_proxy_->OnRenderedFrame(frame);
-
- fake_clock_.AdvanceTimeMilliseconds(30);
- statistics_proxy_->OnRenderedFrame(frame);
-
- fake_clock_.AdvanceTimeMilliseconds(5000);
- statistics_proxy_->OnStreamInactive();
-
- stats = statistics_proxy_->GetStats();
- EXPECT_EQ(0u, stats.pause_count);
- EXPECT_EQ(0u, stats.total_pauses_duration_ms);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, ReportsFramesDuration) {
- VideoReceiveStreamInterface::Stats stats = statistics_proxy_->GetStats();
- ASSERT_EQ(0u, stats.total_frames_duration_ms);
-
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- // Emulate delay before first frame is rendered. This is needed to ensure
- // that frame duration only covers time since first frame is rendered and
- // not the total time.
- fake_clock_.AdvanceTimeMilliseconds(5432);
-
- for (int i = 0; i <= 10; ++i) {
- fake_clock_.AdvanceTimeMilliseconds(30);
- statistics_proxy_->OnRenderedFrame(frame);
- }
-
- stats = statistics_proxy_->GetStats();
- EXPECT_EQ(10 * 30u, stats.total_frames_duration_ms);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, ReportsSumSquaredFrameDurations) {
- VideoReceiveStreamInterface::Stats stats = statistics_proxy_->GetStats();
- ASSERT_EQ(0u, stats.sum_squared_frame_durations);
-
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- for (int i = 0; i <= 10; ++i) {
- fake_clock_.AdvanceTimeMilliseconds(30);
- statistics_proxy_->OnRenderedFrame(frame);
- }
-
- stats = statistics_proxy_->GetStats();
- const double kExpectedSumSquaredFrameDurationsSecs =
- 10 * (30 / 1000.0 * 30 / 1000.0);
- EXPECT_EQ(kExpectedSumSquaredFrameDurationsSecs,
- stats.sum_squared_frame_durations);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameWithoutQpQpSumWontExist) {
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, OnDecodedFrameWithoutQpResetsQpSum) {
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
- statistics_proxy_->OnDecodedFrame(frame, 3u, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(3u, statistics_proxy_->GetStats().qp_sum);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- EXPECT_EQ(absl::nullopt, statistics_proxy_->GetStats().qp_sum);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, OnRenderedFrameIncreasesFramesRendered) {
- EXPECT_EQ(0u, statistics_proxy_->GetStats().frames_rendered);
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- for (uint32_t i = 1; i <= 3; ++i) {
- statistics_proxy_->OnRenderedFrame(frame);
- EXPECT_EQ(i, statistics_proxy_->GetStats().frames_rendered);
- }
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsSsrc) {
- EXPECT_EQ(kRemoteSsrc, statistics_proxy_->GetStats().ssrc);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsIncomingPayloadType) {
- const int kPayloadType = 111;
- statistics_proxy_->OnIncomingPayloadType(kPayloadType);
- EXPECT_EQ(kPayloadType, statistics_proxy_->GetStats().current_payload_type);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsDecoderImplementationName) {
- const char* kName = "decoderName";
- statistics_proxy_->OnDecoderImplementationName(kName);
- EXPECT_STREQ(
- kName, statistics_proxy_->GetStats().decoder_implementation_name.c_str());
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsOnCompleteFrame) {
- const int kFrameSizeBytes = 1000;
- statistics_proxy_->OnCompleteFrame(true, kFrameSizeBytes,
- VideoContentType::UNSPECIFIED);
- VideoReceiveStreamInterface::Stats stats = statistics_proxy_->GetStats();
- EXPECT_EQ(1, stats.network_frame_rate);
- EXPECT_EQ(1, stats.frame_counts.key_frames);
- EXPECT_EQ(0, stats.frame_counts.delta_frames);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsOnDroppedFrame) {
- unsigned int dropped_frames = 0;
- for (int i = 0; i < 10; ++i) {
- statistics_proxy_->OnDroppedFrames(i);
- dropped_frames += i;
- }
- VideoReceiveStreamInterface::Stats stats = statistics_proxy_->GetStats();
- EXPECT_EQ(dropped_frames, stats.frames_dropped);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsDecodeTimingStats) {
- const int kMaxDecodeMs = 2;
- const int kCurrentDelayMs = 3;
- const int kTargetDelayMs = 4;
- const int kJitterBufferMs = 5;
- const int kMinPlayoutDelayMs = 6;
- const int kRenderDelayMs = 7;
- const int64_t kRttMs = 8;
- statistics_proxy_->OnRttUpdate(kRttMs, 0);
- statistics_proxy_->OnFrameBufferTimingsUpdated(
- kMaxDecodeMs, kCurrentDelayMs, kTargetDelayMs, kJitterBufferMs,
- kMinPlayoutDelayMs, kRenderDelayMs);
- VideoReceiveStreamInterface::Stats stats = statistics_proxy_->GetStats();
- EXPECT_EQ(kMaxDecodeMs, stats.max_decode_ms);
- EXPECT_EQ(kCurrentDelayMs, stats.current_delay_ms);
- EXPECT_EQ(kTargetDelayMs, stats.target_delay_ms);
- EXPECT_EQ(kJitterBufferMs, stats.jitter_buffer_ms);
- EXPECT_EQ(kMinPlayoutDelayMs, stats.min_playout_delay_ms);
- EXPECT_EQ(kRenderDelayMs, stats.render_delay_ms);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsRtcpPacketTypeCounts) {
- const uint32_t kFirPackets = 33;
- const uint32_t kPliPackets = 44;
- const uint32_t kNackPackets = 55;
- RtcpPacketTypeCounter counter;
- counter.fir_packets = kFirPackets;
- counter.pli_packets = kPliPackets;
- counter.nack_packets = kNackPackets;
- statistics_proxy_->RtcpPacketTypesCounterUpdated(kRemoteSsrc, counter);
- VideoReceiveStreamInterface::Stats stats = statistics_proxy_->GetStats();
- EXPECT_EQ(kFirPackets, stats.rtcp_packet_type_counts.fir_packets);
- EXPECT_EQ(kPliPackets, stats.rtcp_packet_type_counts.pli_packets);
- EXPECT_EQ(kNackPackets, stats.rtcp_packet_type_counts.nack_packets);
-}
-
-TEST_F(ReceiveStatisticsProxyTest,
- GetStatsReportsNoRtcpPacketTypeCountsForUnknownSsrc) {
- RtcpPacketTypeCounter counter;
- counter.fir_packets = 33;
- statistics_proxy_->RtcpPacketTypesCounterUpdated(kRemoteSsrc + 1, counter);
- EXPECT_EQ(0u,
- statistics_proxy_->GetStats().rtcp_packet_type_counts.fir_packets);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsFrameCounts) {
- const int kKeyFrames = 3;
- const int kDeltaFrames = 22;
- for (int i = 0; i < kKeyFrames; i++) {
- statistics_proxy_->OnCompleteFrame(true, 0, VideoContentType::UNSPECIFIED);
- }
- for (int i = 0; i < kDeltaFrames; i++) {
- statistics_proxy_->OnCompleteFrame(false, 0, VideoContentType::UNSPECIFIED);
- }
-
- VideoReceiveStreamInterface::Stats stats = statistics_proxy_->GetStats();
- EXPECT_EQ(kKeyFrames, stats.frame_counts.key_frames);
- EXPECT_EQ(kDeltaFrames, stats.frame_counts.delta_frames);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsCName) {
- const char* kName = "cName";
- statistics_proxy_->OnCname(kRemoteSsrc, kName);
- EXPECT_STREQ(kName, statistics_proxy_->GetStats().c_name.c_str());
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsNoCNameForUnknownSsrc) {
- const char* kName = "cName";
- statistics_proxy_->OnCname(kRemoteSsrc + 1, kName);
- EXPECT_STREQ("", statistics_proxy_->GetStats().c_name.c_str());
-}
-
-TEST_F(ReceiveStatisticsProxyTest, ReportsLongestTimingFrameInfo) {
- const int64_t kShortEndToEndDelay = 10;
- const int64_t kMedEndToEndDelay = 20;
- const int64_t kLongEndToEndDelay = 100;
- const uint32_t kExpectedRtpTimestamp = 2;
- TimingFrameInfo info;
- absl::optional<TimingFrameInfo> result;
- info.rtp_timestamp = kExpectedRtpTimestamp - 1;
- info.capture_time_ms = 0;
- info.decode_finish_ms = kShortEndToEndDelay;
- statistics_proxy_->OnTimingFrameInfoUpdated(info);
- info.rtp_timestamp =
- kExpectedRtpTimestamp; // this frame should be reported in the end.
- info.capture_time_ms = 0;
- info.decode_finish_ms = kLongEndToEndDelay;
- statistics_proxy_->OnTimingFrameInfoUpdated(info);
- info.rtp_timestamp = kExpectedRtpTimestamp + 1;
- info.capture_time_ms = 0;
- info.decode_finish_ms = kMedEndToEndDelay;
- statistics_proxy_->OnTimingFrameInfoUpdated(info);
- result = statistics_proxy_->GetStats().timing_frame_info;
- EXPECT_TRUE(result);
- EXPECT_EQ(kExpectedRtpTimestamp, result->rtp_timestamp);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, RespectsReportingIntervalForTimingFrames) {
- TimingFrameInfo info;
- const int64_t kShortEndToEndDelay = 10;
- const uint32_t kExpectedRtpTimestamp = 2;
- const int64_t kShortDelayMs = 1000;
- const int64_t kLongDelayMs = 10000;
- absl::optional<TimingFrameInfo> result;
- info.rtp_timestamp = kExpectedRtpTimestamp;
- info.capture_time_ms = 0;
- info.decode_finish_ms = kShortEndToEndDelay;
- statistics_proxy_->OnTimingFrameInfoUpdated(info);
- fake_clock_.AdvanceTimeMilliseconds(kShortDelayMs);
- result = statistics_proxy_->GetStats().timing_frame_info;
- EXPECT_TRUE(result);
- EXPECT_EQ(kExpectedRtpTimestamp, result->rtp_timestamp);
- fake_clock_.AdvanceTimeMilliseconds(kLongDelayMs);
- result = statistics_proxy_->GetStats().timing_frame_info;
- EXPECT_FALSE(result);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, LifetimeHistogramIsUpdated) {
- const int64_t kTimeSec = 3;
- fake_clock_.AdvanceTimeMilliseconds(kTimeSec * 1000);
- // Need at least one frame to report stream lifetime.
- statistics_proxy_->OnCompleteFrame(true, 1000, VideoContentType::UNSPECIFIED);
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.ReceiveStreamLifetimeInSeconds"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.ReceiveStreamLifetimeInSeconds",
- kTimeSec));
-}
-
-TEST_F(ReceiveStatisticsProxyTest,
- LifetimeHistogramNotReportedForEmptyStreams) {
- const int64_t kTimeSec = 3;
- fake_clock_.AdvanceTimeMilliseconds(kTimeSec * 1000);
- // No frames received.
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(
- 0, metrics::NumSamples("WebRTC.Video.ReceiveStreamLifetimeInSeconds"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, BadCallHistogramsAreUpdated) {
- // Based on the tuning parameters this will produce 7 uncertain states,
- // then 10 certainly bad states. There has to be 10 certain states before
- // any histograms are recorded.
- const int kNumBadSamples = 17;
- // We only count one sample per second.
- const int kBadFameIntervalMs = 1100;
-
- StreamDataCounters counters;
- counters.first_packet_time_ms = fake_clock_.TimeInMilliseconds();
-
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- for (int i = 0; i < kNumBadSamples; ++i) {
- fake_clock_.AdvanceTimeMilliseconds(kBadFameIntervalMs);
- statistics_proxy_->OnRenderedFrame(frame);
- }
- statistics_proxy_->UpdateHistograms(absl::nullopt, counters, nullptr);
- EXPECT_METRIC_EQ(1, metrics::NumSamples("WebRTC.Video.BadCall.Any"));
- EXPECT_METRIC_EQ(1, metrics::NumEvents("WebRTC.Video.BadCall.Any", 100));
-
- EXPECT_METRIC_EQ(1, metrics::NumSamples("WebRTC.Video.BadCall.FrameRate"));
- EXPECT_METRIC_EQ(1,
- metrics::NumEvents("WebRTC.Video.BadCall.FrameRate", 100));
-
- EXPECT_METRIC_EQ(
- 0, metrics::NumSamples("WebRTC.Video.BadCall.FrameRateVariance"));
-
- EXPECT_METRIC_EQ(0, metrics::NumSamples("WebRTC.Video.BadCall.Qp"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, PacketLossHistogramIsUpdated) {
- statistics_proxy_->UpdateHistograms(10, StreamDataCounters(), nullptr);
- EXPECT_METRIC_EQ(
- 0, metrics::NumSamples("WebRTC.Video.ReceivedPacketsLostInPercent"));
-
- // Restart
- SetUp();
-
- // Min run time has passed.
- fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000);
- statistics_proxy_->UpdateHistograms(10, StreamDataCounters(), nullptr);
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.ReceivedPacketsLostInPercent"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.ReceivedPacketsLostInPercent", 10));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsPlayoutTimestamp) {
- const int64_t kVideoNtpMs = 21;
- const int64_t kSyncOffsetMs = 22;
- const double kFreqKhz = 90.0;
- EXPECT_EQ(absl::nullopt,
- statistics_proxy_->GetStats().estimated_playout_ntp_timestamp_ms);
- statistics_proxy_->OnSyncOffsetUpdated(kVideoNtpMs, kSyncOffsetMs, kFreqKhz);
- EXPECT_EQ(kVideoNtpMs,
- statistics_proxy_->GetStats().estimated_playout_ntp_timestamp_ms);
- fake_clock_.AdvanceTimeMilliseconds(13);
- EXPECT_EQ(kVideoNtpMs + 13,
- statistics_proxy_->GetStats().estimated_playout_ntp_timestamp_ms);
- fake_clock_.AdvanceTimeMilliseconds(5);
- EXPECT_EQ(kVideoNtpMs + 13 + 5,
- statistics_proxy_->GetStats().estimated_playout_ntp_timestamp_ms);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsAvSyncOffset) {
- const int64_t kVideoNtpMs = 21;
- const int64_t kSyncOffsetMs = 22;
- const double kFreqKhz = 90.0;
- EXPECT_EQ(std::numeric_limits<int>::max(),
- statistics_proxy_->GetStats().sync_offset_ms);
- statistics_proxy_->OnSyncOffsetUpdated(kVideoNtpMs, kSyncOffsetMs, kFreqKhz);
- EXPECT_EQ(kSyncOffsetMs, statistics_proxy_->GetStats().sync_offset_ms);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, AvSyncOffsetHistogramIsUpdated) {
- const int64_t kVideoNtpMs = 21;
- const int64_t kSyncOffsetMs = 22;
- const double kFreqKhz = 90.0;
- for (int i = 0; i < kMinRequiredSamples; ++i)
- statistics_proxy_->OnSyncOffsetUpdated(kVideoNtpMs, kSyncOffsetMs,
- kFreqKhz);
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(1, metrics::NumSamples("WebRTC.Video.AVSyncOffsetInMs"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.AVSyncOffsetInMs", kSyncOffsetMs));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, RtpToNtpFrequencyOffsetHistogramIsUpdated) {
- const int64_t kVideoNtpMs = 21;
- const int64_t kSyncOffsetMs = 22;
- const double kFreqKhz = 90.0;
- statistics_proxy_->OnSyncOffsetUpdated(kVideoNtpMs, kSyncOffsetMs, kFreqKhz);
- statistics_proxy_->OnSyncOffsetUpdated(kVideoNtpMs, kSyncOffsetMs,
- kFreqKhz + 2.2);
- fake_clock_.AdvanceTimeMilliseconds(kFreqOffsetProcessIntervalInMs);
- // Process interval passed, max diff: 2.
- statistics_proxy_->OnSyncOffsetUpdated(kVideoNtpMs, kSyncOffsetMs,
- kFreqKhz + 1.1);
- statistics_proxy_->OnSyncOffsetUpdated(kVideoNtpMs, kSyncOffsetMs,
- kFreqKhz - 4.2);
- statistics_proxy_->OnSyncOffsetUpdated(kVideoNtpMs, kSyncOffsetMs,
- kFreqKhz - 0.9);
- fake_clock_.AdvanceTimeMilliseconds(kFreqOffsetProcessIntervalInMs);
- // Process interval passed, max diff: 4.
- statistics_proxy_->OnSyncOffsetUpdated(kVideoNtpMs, kSyncOffsetMs, kFreqKhz);
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- // Average reported: (2 + 4) / 2 = 3.
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.RtpToNtpFreqOffsetInKhz"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.RtpToNtpFreqOffsetInKhz", 3));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, Vp8QpHistogramIsUpdated) {
- const int kQp = 22;
-
- for (int i = 0; i < kMinRequiredSamples; ++i)
- statistics_proxy_->OnPreDecode(kVideoCodecVP8, kQp);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(1, metrics::NumSamples("WebRTC.Video.Decoded.Vp8.Qp"));
- EXPECT_METRIC_EQ(1, metrics::NumEvents("WebRTC.Video.Decoded.Vp8.Qp", kQp));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, Vp8QpHistogramIsNotUpdatedForTooFewSamples) {
- const int kQp = 22;
-
- for (int i = 0; i < kMinRequiredSamples - 1; ++i)
- statistics_proxy_->OnPreDecode(kVideoCodecVP8, kQp);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(0, metrics::NumSamples("WebRTC.Video.Decoded.Vp8.Qp"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, Vp8QpHistogramIsNotUpdatedIfNoQpValue) {
- for (int i = 0; i < kMinRequiredSamples; ++i)
- statistics_proxy_->OnPreDecode(kVideoCodecVP8, -1);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(0, metrics::NumSamples("WebRTC.Video.Decoded.Vp8.Qp"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest,
- KeyFrameHistogramNotUpdatedForTooFewSamples) {
- const bool kIsKeyFrame = false;
- const int kFrameSizeBytes = 1000;
-
- for (int i = 0; i < kMinRequiredSamples - 1; ++i)
- statistics_proxy_->OnCompleteFrame(kIsKeyFrame, kFrameSizeBytes,
- VideoContentType::UNSPECIFIED);
-
- EXPECT_EQ(0, statistics_proxy_->GetStats().frame_counts.key_frames);
- EXPECT_EQ(kMinRequiredSamples - 1,
- statistics_proxy_->GetStats().frame_counts.delta_frames);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(
- 0, metrics::NumSamples("WebRTC.Video.KeyFramesReceivedInPermille"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest,
- KeyFrameHistogramUpdatedForMinRequiredSamples) {
- const bool kIsKeyFrame = false;
- const int kFrameSizeBytes = 1000;
-
- for (int i = 0; i < kMinRequiredSamples; ++i)
- statistics_proxy_->OnCompleteFrame(kIsKeyFrame, kFrameSizeBytes,
- VideoContentType::UNSPECIFIED);
-
- EXPECT_EQ(0, statistics_proxy_->GetStats().frame_counts.key_frames);
- EXPECT_EQ(kMinRequiredSamples,
- statistics_proxy_->GetStats().frame_counts.delta_frames);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.KeyFramesReceivedInPermille"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.KeyFramesReceivedInPermille", 0));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, KeyFrameHistogramIsUpdated) {
- const int kFrameSizeBytes = 1000;
-
- for (int i = 0; i < kMinRequiredSamples; ++i)
- statistics_proxy_->OnCompleteFrame(true, kFrameSizeBytes,
- VideoContentType::UNSPECIFIED);
-
- for (int i = 0; i < kMinRequiredSamples; ++i)
- statistics_proxy_->OnCompleteFrame(false, kFrameSizeBytes,
- VideoContentType::UNSPECIFIED);
-
- EXPECT_EQ(kMinRequiredSamples,
- statistics_proxy_->GetStats().frame_counts.key_frames);
- EXPECT_EQ(kMinRequiredSamples,
- statistics_proxy_->GetStats().frame_counts.delta_frames);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.KeyFramesReceivedInPermille"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.KeyFramesReceivedInPermille", 500));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, TimingHistogramsNotUpdatedForTooFewSamples) {
- const int kMaxDecodeMs = 2;
- const int kCurrentDelayMs = 3;
- const int kTargetDelayMs = 4;
- const int kJitterBufferMs = 5;
- const int kMinPlayoutDelayMs = 6;
- const int kRenderDelayMs = 7;
-
- for (int i = 0; i < kMinRequiredSamples - 1; ++i) {
- statistics_proxy_->OnFrameBufferTimingsUpdated(
- kMaxDecodeMs, kCurrentDelayMs, kTargetDelayMs, kJitterBufferMs,
- kMinPlayoutDelayMs, kRenderDelayMs);
- }
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(0, metrics::NumSamples("WebRTC.Video.DecodeTimeInMs"));
- EXPECT_METRIC_EQ(0,
- metrics::NumSamples("WebRTC.Video.JitterBufferDelayInMs"));
- EXPECT_METRIC_EQ(0, metrics::NumSamples("WebRTC.Video.TargetDelayInMs"));
- EXPECT_METRIC_EQ(0, metrics::NumSamples("WebRTC.Video.CurrentDelayInMs"));
- EXPECT_METRIC_EQ(0, metrics::NumSamples("WebRTC.Video.OnewayDelayInMs"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, TimingHistogramsAreUpdated) {
- const int kMaxDecodeMs = 2;
- const int kCurrentDelayMs = 3;
- const int kTargetDelayMs = 4;
- const int kJitterBufferMs = 5;
- const int kMinPlayoutDelayMs = 6;
- const int kRenderDelayMs = 7;
-
- for (int i = 0; i < kMinRequiredSamples; ++i) {
- statistics_proxy_->OnFrameBufferTimingsUpdated(
- kMaxDecodeMs, kCurrentDelayMs, kTargetDelayMs, kJitterBufferMs,
- kMinPlayoutDelayMs, kRenderDelayMs);
- }
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.JitterBufferDelayInMs"));
- EXPECT_METRIC_EQ(1, metrics::NumSamples("WebRTC.Video.TargetDelayInMs"));
- EXPECT_METRIC_EQ(1, metrics::NumSamples("WebRTC.Video.CurrentDelayInMs"));
- EXPECT_METRIC_EQ(1, metrics::NumSamples("WebRTC.Video.OnewayDelayInMs"));
-
- EXPECT_METRIC_EQ(1, metrics::NumEvents("WebRTC.Video.JitterBufferDelayInMs",
- kJitterBufferMs));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.TargetDelayInMs", kTargetDelayMs));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.CurrentDelayInMs", kCurrentDelayMs));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.OnewayDelayInMs", kTargetDelayMs));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, DoesNotReportStaleFramerates) {
- const int kDefaultFps = 30;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- for (int i = 0; i < kDefaultFps; ++i) {
- // Since OnRenderedFrame is never called the fps in each sample will be 0,
- // i.e. bad
- frame.set_ntp_time_ms(fake_clock_.CurrentNtpInMilliseconds());
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
- statistics_proxy_->OnRenderedFrame(frame);
- fake_clock_.AdvanceTimeMilliseconds(1000 / kDefaultFps);
- }
-
- EXPECT_EQ(kDefaultFps, statistics_proxy_->GetStats().decode_frame_rate);
- EXPECT_EQ(kDefaultFps, statistics_proxy_->GetStats().render_frame_rate);
-
- // FPS trackers in stats proxy have a 1000ms sliding window.
- fake_clock_.AdvanceTimeMilliseconds(1000);
- EXPECT_EQ(0, statistics_proxy_->GetStats().decode_frame_rate);
- EXPECT_EQ(0, statistics_proxy_->GetStats().render_frame_rate);
-}
-
-TEST_F(ReceiveStatisticsProxyTest, GetStatsReportsReceivedFrameStats) {
- EXPECT_EQ(0, statistics_proxy_->GetStats().width);
- EXPECT_EQ(0, statistics_proxy_->GetStats().height);
- EXPECT_EQ(0u, statistics_proxy_->GetStats().frames_rendered);
-
- statistics_proxy_->OnRenderedFrame(CreateFrame(kWidth, kHeight));
-
- EXPECT_EQ(kWidth, statistics_proxy_->GetStats().width);
- EXPECT_EQ(kHeight, statistics_proxy_->GetStats().height);
- EXPECT_EQ(1u, statistics_proxy_->GetStats().frames_rendered);
-}
-
-TEST_F(ReceiveStatisticsProxyTest,
- ReceivedFrameHistogramsAreNotUpdatedForTooFewSamples) {
- for (int i = 0; i < kMinRequiredSamples - 1; ++i)
- statistics_proxy_->OnRenderedFrame(CreateFrame(kWidth, kHeight));
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(0,
- metrics::NumSamples("WebRTC.Video.ReceivedWidthInPixels"));
- EXPECT_METRIC_EQ(0,
- metrics::NumSamples("WebRTC.Video.ReceivedHeightInPixels"));
- EXPECT_METRIC_EQ(0,
- metrics::NumSamples("WebRTC.Video.RenderFramesPerSecond"));
- EXPECT_METRIC_EQ(
- 0, metrics::NumSamples("WebRTC.Video.RenderSqrtPixelsPerSecond"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, ReceivedFrameHistogramsAreUpdated) {
- for (int i = 0; i < kMinRequiredSamples; ++i)
- statistics_proxy_->OnRenderedFrame(CreateFrame(kWidth, kHeight));
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.ReceivedWidthInPixels"));
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.ReceivedHeightInPixels"));
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.RenderFramesPerSecond"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.RenderSqrtPixelsPerSecond"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.ReceivedWidthInPixels", kWidth));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.ReceivedHeightInPixels", kHeight));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, ZeroDelayReportedIfFrameNotDelayed) {
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
-
- // Frame not delayed, delayed frames to render: 0%.
- const int64_t kNowMs = fake_clock_.TimeInMilliseconds();
- statistics_proxy_->OnRenderedFrame(CreateFrameWithRenderTimeMs(kNowMs));
-
- // Min run time has passed.
- fake_clock_.AdvanceTimeMilliseconds((metrics::kMinRunTimeInSeconds * 1000));
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.DelayedFramesToRenderer"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.DelayedFramesToRenderer", 0));
- EXPECT_METRIC_EQ(0, metrics::NumSamples(
- "WebRTC.Video.DelayedFramesToRenderer_AvgDelayInMs"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest,
- DelayedFrameHistogramsAreNotUpdatedIfMinRuntimeHasNotPassed) {
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
-
- // Frame not delayed, delayed frames to render: 0%.
- const int64_t kNowMs = fake_clock_.TimeInMilliseconds();
- statistics_proxy_->OnRenderedFrame(CreateFrameWithRenderTimeMs(kNowMs));
-
- // Min run time has not passed.
- fake_clock_.AdvanceTimeMilliseconds((metrics::kMinRunTimeInSeconds * 1000) -
- 1);
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(0,
- metrics::NumSamples("WebRTC.Video.DelayedFramesToRenderer"));
- EXPECT_METRIC_EQ(0, metrics::NumSamples(
- "WebRTC.Video.DelayedFramesToRenderer_AvgDelayInMs"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest,
- DelayedFramesHistogramsAreNotUpdatedIfNoRenderedFrames) {
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
-
- // Min run time has passed. No rendered frames.
- fake_clock_.AdvanceTimeMilliseconds((metrics::kMinRunTimeInSeconds * 1000));
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(0,
- metrics::NumSamples("WebRTC.Video.DelayedFramesToRenderer"));
- EXPECT_METRIC_EQ(0, metrics::NumSamples(
- "WebRTC.Video.DelayedFramesToRenderer_AvgDelayInMs"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, DelayReportedIfFrameIsDelayed) {
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
-
- // Frame delayed 1 ms, delayed frames to render: 100%.
- const int64_t kNowMs = fake_clock_.TimeInMilliseconds();
- statistics_proxy_->OnRenderedFrame(CreateFrameWithRenderTimeMs(kNowMs - 1));
-
- // Min run time has passed.
- fake_clock_.AdvanceTimeMilliseconds((metrics::kMinRunTimeInSeconds * 1000));
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.DelayedFramesToRenderer"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.DelayedFramesToRenderer", 100));
- EXPECT_METRIC_EQ(1, metrics::NumSamples(
- "WebRTC.Video.DelayedFramesToRenderer_AvgDelayInMs"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.DelayedFramesToRenderer_AvgDelayInMs",
- 1));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, AverageDelayOfDelayedFramesIsReported) {
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0,
- VideoContentType::UNSPECIFIED);
-
- // Two frames delayed (6 ms, 10 ms), delayed frames to render: 50%.
- const int64_t kNowMs = fake_clock_.TimeInMilliseconds();
- statistics_proxy_->OnRenderedFrame(CreateFrameWithRenderTimeMs(kNowMs - 10));
- statistics_proxy_->OnRenderedFrame(CreateFrameWithRenderTimeMs(kNowMs - 6));
- statistics_proxy_->OnRenderedFrame(CreateFrameWithRenderTimeMs(kNowMs));
- statistics_proxy_->OnRenderedFrame(CreateFrameWithRenderTimeMs(kNowMs + 1));
-
- // Min run time has passed.
- fake_clock_.AdvanceTimeMilliseconds((metrics::kMinRunTimeInSeconds * 1000));
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.DelayedFramesToRenderer"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.DelayedFramesToRenderer", 50));
- EXPECT_METRIC_EQ(1, metrics::NumSamples(
- "WebRTC.Video.DelayedFramesToRenderer_AvgDelayInMs"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.DelayedFramesToRenderer_AvgDelayInMs",
- 8));
-}
-
-TEST_F(ReceiveStatisticsProxyTest,
- RtcpHistogramsNotUpdatedIfMinRuntimeHasNotPassed) {
- StreamDataCounters data_counters;
- data_counters.first_packet_time_ms = fake_clock_.TimeInMilliseconds();
-
- fake_clock_.AdvanceTimeMilliseconds((metrics::kMinRunTimeInSeconds * 1000) -
- 1);
-
- RtcpPacketTypeCounter counter;
- statistics_proxy_->RtcpPacketTypesCounterUpdated(kRemoteSsrc, counter);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, data_counters, nullptr);
- EXPECT_METRIC_EQ(0,
- metrics::NumSamples("WebRTC.Video.FirPacketsSentPerMinute"));
- EXPECT_METRIC_EQ(0,
- metrics::NumSamples("WebRTC.Video.PliPacketsSentPerMinute"));
- EXPECT_METRIC_EQ(
- 0, metrics::NumSamples("WebRTC.Video.NackPacketsSentPerMinute"));
-}
-
-TEST_F(ReceiveStatisticsProxyTest, RtcpHistogramsAreUpdated) {
- StreamDataCounters data_counters;
- data_counters.first_packet_time_ms = fake_clock_.TimeInMilliseconds();
- fake_clock_.AdvanceTimeMilliseconds(metrics::kMinRunTimeInSeconds * 1000);
-
- const uint32_t kFirPackets = 100;
- const uint32_t kPliPackets = 200;
- const uint32_t kNackPackets = 300;
-
- RtcpPacketTypeCounter counter;
- counter.fir_packets = kFirPackets;
- counter.pli_packets = kPliPackets;
- counter.nack_packets = kNackPackets;
- statistics_proxy_->RtcpPacketTypesCounterUpdated(kRemoteSsrc, counter);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, data_counters, nullptr);
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.FirPacketsSentPerMinute"));
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.PliPacketsSentPerMinute"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.NackPacketsSentPerMinute"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.FirPacketsSentPerMinute",
- kFirPackets * 60 / metrics::kMinRunTimeInSeconds));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.PliPacketsSentPerMinute",
- kPliPackets * 60 / metrics::kMinRunTimeInSeconds));
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.NackPacketsSentPerMinute",
- kNackPackets * 60 / metrics::kMinRunTimeInSeconds));
-}
-
-class ReceiveStatisticsProxyTestWithFreezeDuration
- : public ReceiveStatisticsProxyTest,
- public ::testing::WithParamInterface<
- std::tuple<uint32_t, uint32_t, uint32_t>> {
- protected:
- const uint32_t frame_duration_ms_ = {std::get<0>(GetParam())};
- const uint32_t freeze_duration_ms_ = {std::get<1>(GetParam())};
- const uint32_t expected_freeze_count_ = {std::get<2>(GetParam())};
-};
-
-// It is a freeze if:
-// frame_duration_ms >= max(3 * avg_frame_duration, avg_frame_duration + 150)
-// where avg_frame_duration is average duration of last 30 frames including
-// the current one.
-//
-// Condition 1: 3 * avg_frame_duration > avg_frame_duration + 150
-const auto kFreezeDetectionCond1Freeze = std::make_tuple(150, 483, 1);
-const auto kFreezeDetectionCond1NotFreeze = std::make_tuple(150, 482, 0);
-// Condition 2: 3 * avg_frame_duration < avg_frame_duration + 150
-const auto kFreezeDetectionCond2Freeze = std::make_tuple(30, 185, 1);
-const auto kFreezeDetectionCond2NotFreeze = std::make_tuple(30, 184, 0);
-
-INSTANTIATE_TEST_SUITE_P(_,
- ReceiveStatisticsProxyTestWithFreezeDuration,
- ::testing::Values(kFreezeDetectionCond1Freeze,
- kFreezeDetectionCond1NotFreeze,
- kFreezeDetectionCond2Freeze,
- kFreezeDetectionCond2NotFreeze));
-
-TEST_P(ReceiveStatisticsProxyTestWithFreezeDuration, FreezeDetection) {
- VideoReceiveStreamInterface::Stats stats = statistics_proxy_->GetStats();
- EXPECT_EQ(0u, stats.freeze_count);
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- // Add a very long frame. This is need to verify that average frame
- // duration, which is supposed to be calculated as mean of durations of
- // last 30 frames, is calculated correctly.
- statistics_proxy_->OnRenderedFrame(frame);
- fake_clock_.AdvanceTimeMilliseconds(2000);
-
- for (size_t i = 0;
- i <= VideoQualityObserver::kAvgInterframeDelaysWindowSizeFrames; ++i) {
- fake_clock_.AdvanceTimeMilliseconds(frame_duration_ms_);
- statistics_proxy_->OnRenderedFrame(frame);
- }
-
- fake_clock_.AdvanceTimeMilliseconds(freeze_duration_ms_);
- statistics_proxy_->OnRenderedFrame(frame);
-
- stats = statistics_proxy_->GetStats();
- EXPECT_EQ(stats.freeze_count, expected_freeze_count_);
-}
-
-class ReceiveStatisticsProxyTestWithContent
- : public ReceiveStatisticsProxyTest,
- public ::testing::WithParamInterface<webrtc::VideoContentType> {
- protected:
- const webrtc::VideoContentType content_type_{GetParam()};
-};
-
-INSTANTIATE_TEST_SUITE_P(ContentTypes,
- ReceiveStatisticsProxyTestWithContent,
- ::testing::Values(VideoContentType::UNSPECIFIED,
- VideoContentType::SCREENSHARE));
-
-TEST_P(ReceiveStatisticsProxyTestWithContent, InterFrameDelaysAreReported) {
- const int kInterFrameDelayMs = 33;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- for (int i = 0; i < kMinRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
- // One extra with double the interval.
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- const int kExpectedInterFrame =
- (kInterFrameDelayMs * (kMinRequiredSamples - 1) +
- kInterFrameDelayMs * 2) /
- kMinRequiredSamples;
- if (videocontenttypehelpers::IsScreenshare(content_type_)) {
- EXPECT_METRIC_EQ(
- kExpectedInterFrame,
- metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(
- kInterFrameDelayMs * 2,
- metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
- } else {
- EXPECT_METRIC_EQ(kExpectedInterFrame,
- metrics::MinSample("WebRTC.Video.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(kInterFrameDelayMs * 2,
- metrics::MinSample("WebRTC.Video.InterframeDelayMaxInMs"));
- }
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent,
- InterFrameDelaysPercentilesAreReported) {
- const int kInterFrameDelayMs = 33;
- const int kLastFivePercentsSamples = kMinRequiredSamples * 5 / 100;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- for (int i = 0; i <= kMinRequiredSamples - kLastFivePercentsSamples; ++i) {
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- }
- // Last 5% of intervals are double in size.
- for (int i = 0; i < kLastFivePercentsSamples; ++i) {
- fake_clock_.AdvanceTimeMilliseconds(2 * kInterFrameDelayMs);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- }
- // Final sample is outlier and 10 times as big.
- fake_clock_.AdvanceTimeMilliseconds(10 * kInterFrameDelayMs);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- const int kExpectedInterFrame = kInterFrameDelayMs * 2;
- if (videocontenttypehelpers::IsScreenshare(content_type_)) {
- EXPECT_METRIC_EQ(
- kExpectedInterFrame,
- metrics::MinSample(
- "WebRTC.Video.Screenshare.InterframeDelay95PercentileInMs"));
- } else {
- EXPECT_METRIC_EQ(
- kExpectedInterFrame,
- metrics::MinSample("WebRTC.Video.InterframeDelay95PercentileInMs"));
- }
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent,
- MaxInterFrameDelayOnlyWithValidAverage) {
- const int kInterFrameDelayMs = 33;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- for (int i = 0; i < kMinRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
-
- // `kMinRequiredSamples` samples, and thereby intervals, is required. That
- // means we're one frame short of having a valid data set.
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(0, metrics::NumSamples("WebRTC.Video.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(0,
- metrics::NumSamples("WebRTC.Video.InterframeDelayMaxInMs"));
- EXPECT_METRIC_EQ(
- 0, metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(0, metrics::NumSamples(
- "WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent, MaxInterFrameDelayOnlyWithPause) {
- const int kInterFrameDelayMs = 33;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- for (int i = 0; i <= kMinRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
-
- // At this state, we should have a valid inter-frame delay.
- // Indicate stream paused and make a large jump in time.
- statistics_proxy_->OnStreamInactive();
- fake_clock_.AdvanceTimeMilliseconds(5000);
-
- // Insert two more frames. The interval during the pause should be disregarded
- // in the stats.
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- if (videocontenttypehelpers::IsScreenshare(content_type_)) {
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(1, metrics::NumSamples(
- "WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
- EXPECT_METRIC_EQ(
- kInterFrameDelayMs,
- metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(
- kInterFrameDelayMs,
- metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
- } else {
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.InterframeDelayMaxInMs"));
- EXPECT_METRIC_EQ(kInterFrameDelayMs,
- metrics::MinSample("WebRTC.Video.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(kInterFrameDelayMs,
- metrics::MinSample("WebRTC.Video.InterframeDelayMaxInMs"));
- }
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent, FreezesAreReported) {
- const int kInterFrameDelayMs = 33;
- const int kFreezeDelayMs = 200;
- const int kCallDurationMs =
- kMinRequiredSamples * kInterFrameDelayMs + kFreezeDelayMs;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- for (int i = 0; i < kMinRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- statistics_proxy_->OnRenderedFrame(frame);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
- // Add extra freeze.
- fake_clock_.AdvanceTimeMilliseconds(kFreezeDelayMs);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- statistics_proxy_->OnRenderedFrame(frame);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- const int kExpectedTimeBetweenFreezes =
- kInterFrameDelayMs * (kMinRequiredSamples - 1);
- const int kExpectedNumberFreezesPerMinute = 60 * 1000 / kCallDurationMs;
- if (videocontenttypehelpers::IsScreenshare(content_type_)) {
- EXPECT_METRIC_EQ(
- kFreezeDelayMs + kInterFrameDelayMs,
- metrics::MinSample("WebRTC.Video.Screenshare.MeanFreezeDurationMs"));
- EXPECT_METRIC_EQ(kExpectedTimeBetweenFreezes,
- metrics::MinSample(
- "WebRTC.Video.Screenshare.MeanTimeBetweenFreezesMs"));
- EXPECT_METRIC_EQ(
- kExpectedNumberFreezesPerMinute,
- metrics::MinSample("WebRTC.Video.Screenshare.NumberFreezesPerMinute"));
- } else {
- EXPECT_METRIC_EQ(kFreezeDelayMs + kInterFrameDelayMs,
- metrics::MinSample("WebRTC.Video.MeanFreezeDurationMs"));
- EXPECT_METRIC_EQ(
- kExpectedTimeBetweenFreezes,
- metrics::MinSample("WebRTC.Video.MeanTimeBetweenFreezesMs"));
- EXPECT_METRIC_EQ(kExpectedNumberFreezesPerMinute,
- metrics::MinSample("WebRTC.Video.NumberFreezesPerMinute"));
- }
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent, HarmonicFrameRateIsReported) {
- const int kFrameDurationMs = 33;
- const int kFreezeDurationMs = 200;
- const int kPauseDurationMs = 10000;
- const int kCallDurationMs = kMinRequiredSamples * kFrameDurationMs +
- kFreezeDurationMs + kPauseDurationMs;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- for (int i = 0; i < kMinRequiredSamples; ++i) {
- fake_clock_.AdvanceTimeMilliseconds(kFrameDurationMs);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- statistics_proxy_->OnRenderedFrame(frame);
- }
-
- // Freezes and pauses should be included into harmonic frame rate.
- // Add freeze.
- fake_clock_.AdvanceTimeMilliseconds(kFreezeDurationMs);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- statistics_proxy_->OnRenderedFrame(frame);
-
- // Add pause.
- fake_clock_.AdvanceTimeMilliseconds(kPauseDurationMs);
- statistics_proxy_->OnStreamInactive();
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- statistics_proxy_->OnRenderedFrame(frame);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- double kSumSquaredFrameDurationSecs =
- (kMinRequiredSamples - 1) *
- (kFrameDurationMs / 1000.0 * kFrameDurationMs / 1000.0);
- kSumSquaredFrameDurationSecs +=
- kFreezeDurationMs / 1000.0 * kFreezeDurationMs / 1000.0;
- kSumSquaredFrameDurationSecs +=
- kPauseDurationMs / 1000.0 * kPauseDurationMs / 1000.0;
- const int kExpectedHarmonicFrameRateFps =
- std::round(kCallDurationMs / (1000 * kSumSquaredFrameDurationSecs));
- if (videocontenttypehelpers::IsScreenshare(content_type_)) {
- EXPECT_METRIC_EQ(
- kExpectedHarmonicFrameRateFps,
- metrics::MinSample("WebRTC.Video.Screenshare.HarmonicFrameRate"));
- } else {
- EXPECT_METRIC_EQ(kExpectedHarmonicFrameRateFps,
- metrics::MinSample("WebRTC.Video.HarmonicFrameRate"));
- }
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent, PausesAreIgnored) {
- const int kInterFrameDelayMs = 33;
- const int kPauseDurationMs = 10000;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- for (int i = 0; i <= kMinRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- statistics_proxy_->OnRenderedFrame(frame);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
- // Add a pause.
- fake_clock_.AdvanceTimeMilliseconds(kPauseDurationMs);
- statistics_proxy_->OnStreamInactive();
-
- // Second playback interval with triple the length.
- for (int i = 0; i <= kMinRequiredSamples * 3; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- statistics_proxy_->OnRenderedFrame(frame);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- // Average of two playback intervals.
- const int kExpectedTimeBetweenFreezes =
- kInterFrameDelayMs * kMinRequiredSamples * 2;
- if (videocontenttypehelpers::IsScreenshare(content_type_)) {
- EXPECT_METRIC_EQ(-1, metrics::MinSample(
- "WebRTC.Video.Screenshare.MeanFreezeDurationMs"));
- EXPECT_METRIC_EQ(kExpectedTimeBetweenFreezes,
- metrics::MinSample(
- "WebRTC.Video.Screenshare.MeanTimeBetweenFreezesMs"));
- } else {
- EXPECT_METRIC_EQ(-1,
- metrics::MinSample("WebRTC.Video.MeanFreezeDurationMs"));
- EXPECT_METRIC_EQ(
- kExpectedTimeBetweenFreezes,
- metrics::MinSample("WebRTC.Video.MeanTimeBetweenFreezesMs"));
- }
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent, ManyPausesAtTheBeginning) {
- const int kInterFrameDelayMs = 33;
- const int kPauseDurationMs = 10000;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- for (int i = 0; i <= kMinRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
-
- statistics_proxy_->OnStreamInactive();
- fake_clock_.AdvanceTimeMilliseconds(kPauseDurationMs);
-
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type_);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- // No freezes should be detected, as all long inter-frame delays were pauses.
- if (videocontenttypehelpers::IsScreenshare(content_type_)) {
- EXPECT_METRIC_EQ(-1, metrics::MinSample(
- "WebRTC.Video.Screenshare.MeanFreezeDurationMs"));
- } else {
- EXPECT_METRIC_EQ(-1,
- metrics::MinSample("WebRTC.Video.MeanFreezeDurationMs"));
- }
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent, TimeInHdReported) {
- const int kInterFrameDelayMs = 20;
- webrtc::VideoFrame frame_hd = CreateFrame(1280, 720);
- webrtc::VideoFrame frame_sd = CreateFrame(640, 360);
-
- // HD frames.
- for (int i = 0; i < kMinRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame_hd, absl::nullopt, 0,
- content_type_);
- statistics_proxy_->OnRenderedFrame(frame_hd);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
- // SD frames.
- for (int i = 0; i < 2 * kMinRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame_sd, absl::nullopt, 0,
- content_type_);
- statistics_proxy_->OnRenderedFrame(frame_sd);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
- // Extra last frame.
- statistics_proxy_->OnRenderedFrame(frame_sd);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- const int kExpectedTimeInHdPercents = 33;
- if (videocontenttypehelpers::IsScreenshare(content_type_)) {
- EXPECT_METRIC_EQ(
- kExpectedTimeInHdPercents,
- metrics::MinSample("WebRTC.Video.Screenshare.TimeInHdPercentage"));
- } else {
- EXPECT_METRIC_EQ(kExpectedTimeInHdPercents,
- metrics::MinSample("WebRTC.Video.TimeInHdPercentage"));
- }
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent, TimeInBlockyVideoReported) {
- const int kInterFrameDelayMs = 20;
- const int kHighQp = 80;
- const int kLowQp = 30;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- // High quality frames.
- for (int i = 0; i < kMinRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, kLowQp, 0, content_type_);
- statistics_proxy_->OnRenderedFrame(frame);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
- // Blocky frames.
- for (int i = 0; i < 2 * kMinRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, kHighQp, 0, content_type_);
- statistics_proxy_->OnRenderedFrame(frame);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
- // Extra last frame.
- statistics_proxy_->OnDecodedFrame(frame, kHighQp, 0, content_type_);
- statistics_proxy_->OnRenderedFrame(frame);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- const int kExpectedTimeInHdPercents = 66;
- if (videocontenttypehelpers::IsScreenshare(content_type_)) {
- EXPECT_METRIC_EQ(
- kExpectedTimeInHdPercents,
- metrics::MinSample(
- "WebRTC.Video.Screenshare.TimeInBlockyVideoPercentage"));
- } else {
- EXPECT_METRIC_EQ(
- kExpectedTimeInHdPercents,
- metrics::MinSample("WebRTC.Video.TimeInBlockyVideoPercentage"));
- }
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent, DownscalesReported) {
- const int kInterFrameDelayMs = 2000; // To ensure long enough call duration.
-
- webrtc::VideoFrame frame_hd = CreateFrame(1280, 720);
- webrtc::VideoFrame frame_sd = CreateFrame(640, 360);
- webrtc::VideoFrame frame_ld = CreateFrame(320, 180);
-
- // Call once to pass content type.
- statistics_proxy_->OnDecodedFrame(frame_hd, absl::nullopt, 0, content_type_);
-
- statistics_proxy_->OnRenderedFrame(frame_hd);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
-
- // Downscale.
- statistics_proxy_->OnRenderedFrame(frame_sd);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
-
- // Downscale.
- statistics_proxy_->OnRenderedFrame(frame_ld);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
-
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- const int kExpectedDownscales = 30; // 2 per 4 seconds = 30 per minute.
- if (videocontenttypehelpers::IsScreenshare(content_type_)) {
- EXPECT_METRIC_EQ(
- kExpectedDownscales,
- metrics::MinSample(
- "WebRTC.Video.Screenshare.NumberResolutionDownswitchesPerMinute"));
- } else {
- EXPECT_METRIC_EQ(kExpectedDownscales,
- metrics::MinSample(
- "WebRTC.Video.NumberResolutionDownswitchesPerMinute"));
- }
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent, DecodeTimeReported) {
- const int kInterFrameDelayMs = 20;
- const int kLowQp = 30;
- const int kDecodeMs = 7;
-
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- for (int i = 0; i < kMinRequiredSamples; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, kLowQp, kDecodeMs, content_type_);
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs);
- }
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
- EXPECT_METRIC_EQ(
- 1, metrics::NumEvents("WebRTC.Video.DecodeTimeInMs", kDecodeMs));
-}
-
-TEST_P(ReceiveStatisticsProxyTestWithContent,
- StatsAreSlicedOnSimulcastAndExperiment) {
- const uint8_t experiment_id = 1;
- webrtc::VideoContentType content_type = content_type_;
- videocontenttypehelpers::SetExperimentId(&content_type, experiment_id);
- const int kInterFrameDelayMs1 = 30;
- const int kInterFrameDelayMs2 = 50;
- webrtc::VideoFrame frame = CreateFrame(kWidth, kHeight);
-
- videocontenttypehelpers::SetSimulcastId(&content_type, 1);
- for (int i = 0; i <= kMinRequiredSamples; ++i) {
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs1);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type);
- }
-
- videocontenttypehelpers::SetSimulcastId(&content_type, 2);
- for (int i = 0; i <= kMinRequiredSamples; ++i) {
- fake_clock_.AdvanceTimeMilliseconds(kInterFrameDelayMs2);
- statistics_proxy_->OnDecodedFrame(frame, absl::nullopt, 0, content_type);
- }
- statistics_proxy_->UpdateHistograms(absl::nullopt, StreamDataCounters(),
- nullptr);
-
- if (videocontenttypehelpers::IsScreenshare(content_type)) {
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(1, metrics::NumSamples(
- "WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
- EXPECT_METRIC_EQ(1, metrics::NumSamples(
- "WebRTC.Video.Screenshare.InterframeDelayInMs.S0"));
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples(
- "WebRTC.Video.Screenshare.InterframeDelayMaxInMs.S0"));
- EXPECT_METRIC_EQ(1, metrics::NumSamples(
- "WebRTC.Video.Screenshare.InterframeDelayInMs.S1"));
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples(
- "WebRTC.Video.Screenshare.InterframeDelayMaxInMs.S1"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayInMs"
- ".ExperimentGroup0"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayMaxInMs"
- ".ExperimentGroup0"));
- EXPECT_METRIC_EQ(
- kInterFrameDelayMs1,
- metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs.S0"));
- EXPECT_METRIC_EQ(
- kInterFrameDelayMs2,
- metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs.S1"));
- EXPECT_METRIC_EQ(
- (kInterFrameDelayMs1 + kInterFrameDelayMs2) / 2,
- metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(
- kInterFrameDelayMs2,
- metrics::MinSample("WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
- EXPECT_METRIC_EQ(
- (kInterFrameDelayMs1 + kInterFrameDelayMs2) / 2,
- metrics::MinSample(
- "WebRTC.Video.Screenshare.InterframeDelayInMs.ExperimentGroup0"));
- } else {
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.InterframeDelayMaxInMs"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.InterframeDelayInMs.S0"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.InterframeDelayMaxInMs.S0"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.InterframeDelayInMs.S1"));
- EXPECT_METRIC_EQ(
- 1, metrics::NumSamples("WebRTC.Video.InterframeDelayMaxInMs.S1"));
- EXPECT_METRIC_EQ(1, metrics::NumSamples("WebRTC.Video.InterframeDelayInMs"
- ".ExperimentGroup0"));
- EXPECT_METRIC_EQ(1,
- metrics::NumSamples("WebRTC.Video.InterframeDelayMaxInMs"
- ".ExperimentGroup0"));
- EXPECT_METRIC_EQ(kInterFrameDelayMs1,
- metrics::MinSample("WebRTC.Video.InterframeDelayInMs.S0"));
- EXPECT_METRIC_EQ(kInterFrameDelayMs2,
- metrics::MinSample("WebRTC.Video.InterframeDelayInMs.S1"));
- EXPECT_METRIC_EQ((kInterFrameDelayMs1 + kInterFrameDelayMs2) / 2,
- metrics::MinSample("WebRTC.Video.InterframeDelayInMs"));
- EXPECT_METRIC_EQ(kInterFrameDelayMs2,
- metrics::MinSample("WebRTC.Video.InterframeDelayMaxInMs"));
- EXPECT_METRIC_EQ((kInterFrameDelayMs1 + kInterFrameDelayMs2) / 2,
- metrics::MinSample(
- "WebRTC.Video.InterframeDelayInMs.ExperimentGroup0"));
- }
-}
-
-class ReceiveStatisticsProxyTestWithDecodeTimeHistograms
- : public ::testing::WithParamInterface<
- std::tuple<bool, int, int, int, VideoCodecType, std::string>>,
- public ReceiveStatisticsProxyTest {
- public:
- ReceiveStatisticsProxyTestWithDecodeTimeHistograms()
- : ReceiveStatisticsProxyTest(
- std::get<0>(GetParam())
- ? "WebRTC-DecodeTimeHistogramsKillSwitch/Enabled/"
- : "") {}
-
- protected:
- const std::string kUmaPrefix = "WebRTC.Video.DecodeTimePerFrameInMs.";
- const int expected_number_of_samples_ = {std::get<1>(GetParam())};
- const int width_ = {std::get<2>(GetParam())};
- const int height_ = {std::get<3>(GetParam())};
- const VideoCodecType codec_type_ = {std::get<4>(GetParam())};
- const std::string implementation_name_ = {std::get<5>(GetParam())};
- const std::string uma_histogram_name_ =
- kUmaPrefix + (codec_type_ == kVideoCodecVP9 ? "Vp9." : "H264.") +
- (height_ == 2160 ? "4k." : "Hd.") +
- (implementation_name_.compare("ExternalDecoder") == 0 ? "Hw" : "Sw");
-};
-
-TEST_P(ReceiveStatisticsProxyTestWithDecodeTimeHistograms,
- DecodeTimeHistogramsUpdated) {
- constexpr int kNumberOfFrames = 10;
- constexpr int kDecodeTimeMs = 7;
- constexpr int kFrameDurationMs = 1000 / 60;
-
- webrtc::VideoFrame frame = CreateFrame(width_, height_);
-
- statistics_proxy_->OnDecoderImplementationName(implementation_name_.c_str());
- statistics_proxy_->OnPreDecode(codec_type_, /*qp=*/0);
-
- for (int i = 0; i < kNumberOfFrames; ++i) {
- statistics_proxy_->OnDecodedFrame(frame, /*qp=*/absl::nullopt,
- kDecodeTimeMs,
- VideoContentType::UNSPECIFIED);
- fake_clock_.AdvanceTimeMilliseconds(kFrameDurationMs);
- }
-
- EXPECT_METRIC_EQ(expected_number_of_samples_,
- metrics::NumSamples(uma_histogram_name_));
- EXPECT_METRIC_EQ(expected_number_of_samples_,
- metrics::NumEvents(uma_histogram_name_, kDecodeTimeMs));
-}
-
-const auto kVp94kHw = std::make_tuple(/*killswitch=*/false,
- /*expected_number_of_samples=*/10,
- /*width=*/3840,
- /*height=*/2160,
- kVideoCodecVP9,
- /*implementation=*/"ExternalDecoder");
-const auto kVp94kSw = std::make_tuple(/*killswitch=*/false,
- /*expected_number_of_samples=*/10,
- /*width=*/3840,
- /*height=*/2160,
- kVideoCodecVP9,
- /*implementation=*/"libvpx");
-const auto kVp9HdHw = std::make_tuple(/*killswitch=*/false,
- /*expected_number_of_samples=*/10,
- /*width=*/1920,
- /*height=*/1080,
- kVideoCodecVP9,
- /*implementation=*/"ExternalDecoder");
-const auto kVp9HdSw = std::make_tuple(/*killswitch=*/false,
- /*expected_number_of_samples=*/10,
- /*width=*/1920,
- /*height=*/1080,
- kVideoCodecVP9,
- /*implementation=*/"libvpx");
-const auto kH2644kHw = std::make_tuple(/*killswitch=*/false,
- /*expected_number_of_samples=*/10,
- /*width=*/3840,
- /*height=*/2160,
- kVideoCodecH264,
- /*implementation=*/"ExternalDecoder");
-const auto kH2644kSw = std::make_tuple(/*killswitch=*/false,
- /*expected_number_of_samples=*/10,
- /*width=*/3840,
- /*height=*/2160,
- kVideoCodecH264,
- /*implementation=*/"FFmpeg");
-const auto kH264HdHw = std::make_tuple(/*killswitch=*/false,
- /*expected_number_of_samples=*/10,
- /*width=*/1920,
- /*height=*/1080,
- kVideoCodecH264,
- /*implementation=*/"ExternalDecoder");
-const auto kH264HdSw = std::make_tuple(/*killswitch=*/false,
- /*expected_number_of_samples=*/10,
- /*width=*/1920,
- /*height=*/1080,
- kVideoCodecH264,
- /*implementation=*/"FFmpeg");
-
-INSTANTIATE_TEST_SUITE_P(AllHistogramsPopulated,
- ReceiveStatisticsProxyTestWithDecodeTimeHistograms,
- ::testing::Values(kVp94kHw,
- kVp94kSw,
- kVp9HdHw,
- kVp9HdSw,
- kH2644kHw,
- kH2644kSw,
- kH264HdHw,
- kH264HdSw));
-
-const auto kKillswitchDisabled =
- std::make_tuple(/*killswitch=*/false,
- /*expected_number_of_samples=*/10,
- /*width=*/1920,
- /*height=*/1080,
- kVideoCodecVP9,
- /*implementation=*/"libvpx");
-const auto kKillswitchEnabled =
- std::make_tuple(/*killswitch=*/true,
- /*expected_number_of_samples=*/0,
- /*width=*/1920,
- /*height=*/1080,
- kVideoCodecVP9,
- /*implementation=*/"libvpx");
-
-INSTANTIATE_TEST_SUITE_P(KillswitchEffective,
- ReceiveStatisticsProxyTestWithDecodeTimeHistograms,
- ::testing::Values(kKillswitchDisabled,
- kKillswitchEnabled));
-
-} // namespace webrtc
diff --git a/video/video_quality_observer.cc b/video/video_quality_observer.cc
deleted file mode 100644
index be7b08c..0000000
--- a/video/video_quality_observer.cc
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#include "video/video_quality_observer.h"
-
-#include <algorithm>
-#include <cmath>
-#include <cstdint>
-#include <string>
-
-#include "rtc_base/logging.h"
-#include "rtc_base/strings/string_builder.h"
-#include "system_wrappers/include/metrics.h"
-
-namespace webrtc {
-const uint32_t VideoQualityObserver::kMinFrameSamplesToDetectFreeze = 5;
-const uint32_t VideoQualityObserver::kMinIncreaseForFreezeMs = 150;
-const uint32_t VideoQualityObserver::kAvgInterframeDelaysWindowSizeFrames = 30;
-
-namespace {
-constexpr int kMinVideoDurationMs = 3000;
-constexpr int kMinRequiredSamples = 1;
-constexpr int kPixelsInHighResolution =
- 960 * 540; // CPU-adapted HD still counts.
-constexpr int kPixelsInMediumResolution = 640 * 360;
-constexpr int kBlockyQpThresholdVp8 = 70;
-constexpr int kBlockyQpThresholdVp9 = 180;
-constexpr int kMaxNumCachedBlockyFrames = 100;
-// TODO(ilnik): Add H264/HEVC thresholds.
-} // namespace
-
-VideoQualityObserver::VideoQualityObserver(VideoContentType content_type)
- : last_frame_rendered_ms_(-1),
- num_frames_rendered_(0),
- first_frame_rendered_ms_(-1),
- last_frame_pixels_(0),
- is_last_frame_blocky_(false),
- last_unfreeze_time_ms_(0),
- render_interframe_delays_(kAvgInterframeDelaysWindowSizeFrames),
- sum_squared_interframe_delays_secs_(0.0),
- time_in_resolution_ms_(3, 0),
- current_resolution_(Resolution::Low),
- num_resolution_downgrades_(0),
- time_in_blocky_video_ms_(0),
- content_type_(content_type),
- is_paused_(false) {}
-
-void VideoQualityObserver::UpdateHistograms() {
- // Don't report anything on an empty video stream.
- if (num_frames_rendered_ == 0) {
- return;
- }
-
- char log_stream_buf[2 * 1024];
- rtc::SimpleStringBuilder log_stream(log_stream_buf);
-
- if (last_frame_rendered_ms_ > last_unfreeze_time_ms_) {
- smooth_playback_durations_.Add(last_frame_rendered_ms_ -
- last_unfreeze_time_ms_);
- }
-
- std::string uma_prefix = videocontenttypehelpers::IsScreenshare(content_type_)
- ? "WebRTC.Video.Screenshare"
- : "WebRTC.Video";
-
- auto mean_time_between_freezes =
- smooth_playback_durations_.Avg(kMinRequiredSamples);
- if (mean_time_between_freezes) {
- RTC_HISTOGRAM_COUNTS_SPARSE_100000(uma_prefix + ".MeanTimeBetweenFreezesMs",
- *mean_time_between_freezes);
- log_stream << uma_prefix << ".MeanTimeBetweenFreezesMs "
- << *mean_time_between_freezes << "\n";
- }
- auto avg_freeze_length = freezes_durations_.Avg(kMinRequiredSamples);
- if (avg_freeze_length) {
- RTC_HISTOGRAM_COUNTS_SPARSE_100000(uma_prefix + ".MeanFreezeDurationMs",
- *avg_freeze_length);
- log_stream << uma_prefix << ".MeanFreezeDurationMs " << *avg_freeze_length
- << "\n";
- }
-
- int64_t video_duration_ms =
- last_frame_rendered_ms_ - first_frame_rendered_ms_;
-
- if (video_duration_ms >= kMinVideoDurationMs) {
- int time_spent_in_hd_percentage = static_cast<int>(
- time_in_resolution_ms_[Resolution::High] * 100 / video_duration_ms);
- RTC_HISTOGRAM_COUNTS_SPARSE_100(uma_prefix + ".TimeInHdPercentage",
- time_spent_in_hd_percentage);
- log_stream << uma_prefix << ".TimeInHdPercentage "
- << time_spent_in_hd_percentage << "\n";
-
- int time_with_blocky_video_percentage =
- static_cast<int>(time_in_blocky_video_ms_ * 100 / video_duration_ms);
- RTC_HISTOGRAM_COUNTS_SPARSE_100(uma_prefix + ".TimeInBlockyVideoPercentage",
- time_with_blocky_video_percentage);
- log_stream << uma_prefix << ".TimeInBlockyVideoPercentage "
- << time_with_blocky_video_percentage << "\n";
-
- int num_resolution_downgrades_per_minute =
- num_resolution_downgrades_ * 60000 / video_duration_ms;
- RTC_HISTOGRAM_COUNTS_SPARSE_100(
- uma_prefix + ".NumberResolutionDownswitchesPerMinute",
- num_resolution_downgrades_per_minute);
- log_stream << uma_prefix << ".NumberResolutionDownswitchesPerMinute "
- << num_resolution_downgrades_per_minute << "\n";
-
- int num_freezes_per_minute =
- freezes_durations_.NumSamples() * 60000 / video_duration_ms;
- RTC_HISTOGRAM_COUNTS_SPARSE_100(uma_prefix + ".NumberFreezesPerMinute",
- num_freezes_per_minute);
- log_stream << uma_prefix << ".NumberFreezesPerMinute "
- << num_freezes_per_minute << "\n";
-
- if (sum_squared_interframe_delays_secs_ > 0.0) {
- int harmonic_framerate_fps = std::round(
- video_duration_ms / (1000 * sum_squared_interframe_delays_secs_));
- RTC_HISTOGRAM_COUNTS_SPARSE_100(uma_prefix + ".HarmonicFrameRate",
- harmonic_framerate_fps);
- log_stream << uma_prefix << ".HarmonicFrameRate "
- << harmonic_framerate_fps << "\n";
- }
- }
- RTC_LOG(LS_INFO) << log_stream.str();
-}
-
-void VideoQualityObserver::OnRenderedFrame(const VideoFrame& frame,
- int64_t now_ms) {
- RTC_DCHECK_LE(last_frame_rendered_ms_, now_ms);
- RTC_DCHECK_LE(last_unfreeze_time_ms_, now_ms);
-
- if (num_frames_rendered_ == 0) {
- first_frame_rendered_ms_ = last_unfreeze_time_ms_ = now_ms;
- }
-
- auto blocky_frame_it = blocky_frames_.find(frame.timestamp());
-
- if (num_frames_rendered_ > 0) {
- // Process inter-frame delay.
- const int64_t interframe_delay_ms = now_ms - last_frame_rendered_ms_;
- const double interframe_delays_secs = interframe_delay_ms / 1000.0;
-
- // Sum of squared inter frame intervals is used to calculate the harmonic
- // frame rate metric. The metric aims to reflect overall experience related
- // to smoothness of video playback and includes both freezes and pauses.
- sum_squared_interframe_delays_secs_ +=
- interframe_delays_secs * interframe_delays_secs;
-
- if (!is_paused_) {
- render_interframe_delays_.AddSample(interframe_delay_ms);
-
- bool was_freeze = false;
- if (render_interframe_delays_.Size() >= kMinFrameSamplesToDetectFreeze) {
- const absl::optional<int64_t> avg_interframe_delay =
- render_interframe_delays_.GetAverageRoundedDown();
- RTC_DCHECK(avg_interframe_delay);
- was_freeze = interframe_delay_ms >=
- std::max(3 * *avg_interframe_delay,
- *avg_interframe_delay + kMinIncreaseForFreezeMs);
- }
-
- if (was_freeze) {
- freezes_durations_.Add(interframe_delay_ms);
- smooth_playback_durations_.Add(last_frame_rendered_ms_ -
- last_unfreeze_time_ms_);
- last_unfreeze_time_ms_ = now_ms;
- } else {
- // Count spatial metrics if there were no freeze.
- time_in_resolution_ms_[current_resolution_] += interframe_delay_ms;
-
- if (is_last_frame_blocky_) {
- time_in_blocky_video_ms_ += interframe_delay_ms;
- }
- }
- }
- }
-
- if (is_paused_) {
- // If the stream was paused since the previous frame, do not count the
- // pause toward smooth playback. Explicitly count the part before it and
- // start the new smooth playback interval from this frame.
- is_paused_ = false;
- if (last_frame_rendered_ms_ > last_unfreeze_time_ms_) {
- smooth_playback_durations_.Add(last_frame_rendered_ms_ -
- last_unfreeze_time_ms_);
- }
- last_unfreeze_time_ms_ = now_ms;
-
- if (num_frames_rendered_ > 0) {
- pauses_durations_.Add(now_ms - last_frame_rendered_ms_);
- }
- }
-
- int64_t pixels = frame.width() * frame.height();
- if (pixels >= kPixelsInHighResolution) {
- current_resolution_ = Resolution::High;
- } else if (pixels >= kPixelsInMediumResolution) {
- current_resolution_ = Resolution::Medium;
- } else {
- current_resolution_ = Resolution::Low;
- }
-
- if (pixels < last_frame_pixels_) {
- ++num_resolution_downgrades_;
- }
-
- last_frame_pixels_ = pixels;
- last_frame_rendered_ms_ = now_ms;
-
- is_last_frame_blocky_ = blocky_frame_it != blocky_frames_.end();
- if (is_last_frame_blocky_) {
- blocky_frames_.erase(blocky_frames_.begin(), ++blocky_frame_it);
- }
-
- ++num_frames_rendered_;
-}
-
-void VideoQualityObserver::OnDecodedFrame(const VideoFrame& frame,
- absl::optional<uint8_t> qp,
- VideoCodecType codec) {
- if (qp) {
- absl::optional<int> qp_blocky_threshold;
- // TODO(ilnik): add other codec types when we have QP for them.
- switch (codec) {
- case kVideoCodecVP8:
- qp_blocky_threshold = kBlockyQpThresholdVp8;
- break;
- case kVideoCodecVP9:
- qp_blocky_threshold = kBlockyQpThresholdVp9;
- break;
- default:
- qp_blocky_threshold = absl::nullopt;
- }
-
- RTC_DCHECK(blocky_frames_.find(frame.timestamp()) == blocky_frames_.end());
-
- if (qp_blocky_threshold && *qp > *qp_blocky_threshold) {
- // Cache blocky frame. Its duration will be calculated in render callback.
- if (blocky_frames_.size() > kMaxNumCachedBlockyFrames) {
- RTC_LOG(LS_WARNING) << "Overflow of blocky frames cache.";
- blocky_frames_.erase(
- blocky_frames_.begin(),
- std::next(blocky_frames_.begin(), kMaxNumCachedBlockyFrames / 2));
- }
-
- blocky_frames_.insert(frame.timestamp());
- }
- }
-}
-
-void VideoQualityObserver::OnStreamInactive() {
- is_paused_ = true;
-}
-
-uint32_t VideoQualityObserver::NumFreezes() const {
- return freezes_durations_.NumSamples();
-}
-
-uint32_t VideoQualityObserver::NumPauses() const {
- return pauses_durations_.NumSamples();
-}
-
-uint32_t VideoQualityObserver::TotalFreezesDurationMs() const {
- return freezes_durations_.Sum(kMinRequiredSamples).value_or(0);
-}
-
-uint32_t VideoQualityObserver::TotalPausesDurationMs() const {
- return pauses_durations_.Sum(kMinRequiredSamples).value_or(0);
-}
-
-uint32_t VideoQualityObserver::TotalFramesDurationMs() const {
- return last_frame_rendered_ms_ - first_frame_rendered_ms_;
-}
-
-double VideoQualityObserver::SumSquaredFrameDurationsSec() const {
- return sum_squared_interframe_delays_secs_;
-}
-
-} // namespace webrtc
diff --git a/video/video_quality_observer.h b/video/video_quality_observer.h
deleted file mode 100644
index 6494a6f..0000000
--- a/video/video_quality_observer.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
- *
- * Use of this source code is governed by a BSD-style license
- * that can be found in the LICENSE file in the root of the source
- * tree. An additional intellectual property rights grant can be found
- * in the file PATENTS. All contributing project authors may
- * be found in the AUTHORS file in the root of the source tree.
- */
-
-#ifndef VIDEO_VIDEO_QUALITY_OBSERVER_H_
-#define VIDEO_VIDEO_QUALITY_OBSERVER_H_
-
-#include <stdint.h>
-
-#include <set>
-#include <vector>
-
-#include "absl/types/optional.h"
-#include "api/video/video_codec_type.h"
-#include "api/video/video_content_type.h"
-#include "api/video/video_frame.h"
-#include "rtc_base/numerics/moving_average.h"
-#include "rtc_base/numerics/sample_counter.h"
-
-namespace webrtc {
-
-// Calculates spatial and temporal quality metrics and reports them to UMA
-// stats.
-class VideoQualityObserver {
- public:
- // Use either VideoQualityObserver::kBlockyQpThresholdVp8 or
- // VideoQualityObserver::kBlockyQpThresholdVp9.
- explicit VideoQualityObserver(VideoContentType content_type);
- ~VideoQualityObserver() = default;
-
- void OnDecodedFrame(const VideoFrame& frame,
- absl::optional<uint8_t> qp,
- VideoCodecType codec);
-
- void OnRenderedFrame(const VideoFrame& frame, int64_t now_ms);
-
- void OnStreamInactive();
-
- uint32_t NumFreezes() const;
- uint32_t NumPauses() const;
- uint32_t TotalFreezesDurationMs() const;
- uint32_t TotalPausesDurationMs() const;
- uint32_t TotalFramesDurationMs() const;
- double SumSquaredFrameDurationsSec() const;
-
- void UpdateHistograms();
-
- static const uint32_t kMinFrameSamplesToDetectFreeze;
- static const uint32_t kMinIncreaseForFreezeMs;
- static const uint32_t kAvgInterframeDelaysWindowSizeFrames;
-
- private:
- enum Resolution {
- Low = 0,
- Medium = 1,
- High = 2,
- };
-
- int64_t last_frame_rendered_ms_;
- int64_t num_frames_rendered_;
- int64_t first_frame_rendered_ms_;
- int64_t last_frame_pixels_;
- bool is_last_frame_blocky_;
- // Decoded timestamp of the last delayed frame.
- int64_t last_unfreeze_time_ms_;
- rtc::MovingAverage render_interframe_delays_;
- double sum_squared_interframe_delays_secs_;
- // An inter-frame delay is counted as a freeze if it's significantly longer
- // than average inter-frame delay.
- rtc::SampleCounter freezes_durations_;
- rtc::SampleCounter pauses_durations_;
- // Time between freezes.
- rtc::SampleCounter smooth_playback_durations_;
- // Counters for time spent in different resolutions. Time between each two
- // Consecutive frames is counted to bin corresponding to the first frame
- // resolution.
- std::vector<int64_t> time_in_resolution_ms_;
- // Resolution of the last decoded frame. Resolution enum is used as an index.
- Resolution current_resolution_;
- int num_resolution_downgrades_;
- // Similar to resolution, time spent in high-QP video.
- int64_t time_in_blocky_video_ms_;
- // Content type of the last decoded frame.
- VideoContentType content_type_;
- bool is_paused_;
-
- // Set of decoded frames with high QP value.
- std::set<int64_t> blocky_frames_;
-};
-
-} // namespace webrtc
-
-#endif // VIDEO_VIDEO_QUALITY_OBSERVER_H_