[go: nahoru, domu]

blob: 1dcf3920be762545c5c89e1f992a449624f5d9c3 [file] [log] [blame]
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/peerconnection/stats_collector.h"
#include "base/time/time.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
constexpr float kMinProcessingTimeMs = 1.0f;
constexpr float kExpectedP99ProcessingTimeMs = 12.0f;
constexpr float kP99ToleranceMs = 0.5f;
constexpr media::VideoCodecProfile kCodecProfile =
media::VideoCodecProfile::VP9PROFILE_PROFILE0;
constexpr int kHdWidth = 1280;
constexpr int kHdHeight = 720;
constexpr int kFullHdWidth = 1920;
constexpr int kFullHdHeight = 1080;
constexpr int kFramerate = 30;
constexpr int kFramesPerMinute = kFramerate * 60;
constexpr int kKeyframeInterval = 25;
class StatsCollectorTest : public ::testing::Test {
protected:
StatsCollectorTest()
: mock_now_(base::TimeTicks::Now()),
stats_collector_(
/*is_decode=*/true,
kCodecProfile,
base::BindRepeating(&StatsCollectorTest::StoreProcessingStatsCB,
base::Unretained(this))) {
stats_collector_.StartStatsCollection();
}
void StoreProcessingStatsCB(const StatsCollector::StatsKey& stats_key,
const StatsCollector::VideoStats& video_stats) {
++stats_callbacks_;
last_stats_key_ = stats_key;
last_video_stats_ = video_stats;
}
void ProcessFrames(int width,
int height,
bool is_hw_accelerated,
int frames,
int key_frame_interval,
int frame_rate) {
int pixel_size = width * height;
for (int i = 0; i < frames; ++i) {
bool is_keyframe = i % key_frame_interval == 0;
// Create a distribution with the specified 90th percentile.
float processing_time_ms =
i % 100 < 90 ? kMinProcessingTimeMs : kExpectedP99ProcessingTimeMs;
mock_now_ += base::Milliseconds(1000 / frame_rate);
if (!stats_collector_.stats_collection_finished()) {
stats_collector_.AddProcessingTime(pixel_size, is_hw_accelerated,
processing_time_ms, is_keyframe,
mock_now_);
}
}
}
base::TimeTicks mock_now_;
StatsCollector stats_collector_;
int stats_callbacks_{0};
StatsCollector::StatsKey last_stats_key_;
StatsCollector::VideoStats last_video_stats_;
};
TEST_F(StatsCollectorTest, OneCallbackAfterMinNumberOfFrames) {
constexpr int kFrames = StatsCollector::kMinSamplesThreshold + 10;
EXPECT_EQ(stats_callbacks_, 0);
ProcessFrames(kHdWidth, kHdHeight, /*is_hw_accelerated=*/false, kFrames,
kKeyframeInterval, kFramerate);
// Verify that there's been one stats callback and that the numbers are
// reasonable.
EXPECT_EQ(stats_callbacks_, 1);
EXPECT_TRUE(last_stats_key_.is_decode);
EXPECT_EQ(last_stats_key_.codec_profile, kCodecProfile);
EXPECT_EQ(last_stats_key_.pixel_size, kHdWidth * kHdHeight);
EXPECT_FALSE(last_stats_key_.hw_accelerated);
EXPECT_GE(last_video_stats_.frame_count,
StatsCollector::kMinSamplesThreshold);
EXPECT_LT(last_video_stats_.frame_count, kFrames);
EXPECT_NEAR(last_video_stats_.key_frame_count,
last_video_stats_.frame_count / kKeyframeInterval, 1);
EXPECT_NEAR(last_video_stats_.p99_processing_time_ms,
kExpectedP99ProcessingTimeMs, kP99ToleranceMs);
}
TEST_F(StatsCollectorTest, AtLeastOneCallbackEveryMinute) {
constexpr int kMinutesToRun = 10;
EXPECT_EQ(stats_callbacks_, 0);
int last_stats_callbacks = stats_callbacks_;
int frames_processed = 0;
for (int minute = 0; minute < kMinutesToRun; ++minute) {
ProcessFrames(kHdWidth, kHdHeight, /*is_hw_accelerated=*/false,
kFramesPerMinute, kKeyframeInterval, kFramerate);
frames_processed += kFramesPerMinute;
// Verify that the counter are incremented.
EXPECT_GT(stats_callbacks_, last_stats_callbacks);
last_stats_callbacks = stats_callbacks_;
EXPECT_TRUE(last_stats_key_.is_decode);
EXPECT_EQ(last_stats_key_.codec_profile, kCodecProfile);
EXPECT_EQ(last_stats_key_.pixel_size, kHdWidth * kHdHeight);
EXPECT_FALSE(last_stats_key_.hw_accelerated);
EXPECT_GE(last_video_stats_.frame_count,
frames_processed - kFramesPerMinute / 2);
EXPECT_LT(last_video_stats_.frame_count, frames_processed);
EXPECT_NEAR(last_video_stats_.key_frame_count,
last_video_stats_.frame_count / kKeyframeInterval, 1);
EXPECT_NEAR(last_video_stats_.p99_processing_time_ms,
kExpectedP99ProcessingTimeMs, kP99ToleranceMs);
}
}
TEST_F(StatsCollectorTest, NewReportIfResolutionChanges) {
constexpr int kNumberOfFramesDuringTenSeconds = kFramerate * 10;
EXPECT_EQ(stats_callbacks_, 0);
int last_stats_callbacks = stats_callbacks_;
ProcessFrames(kHdWidth, kHdHeight, /*is_hw_accelerated=*/false,
kNumberOfFramesDuringTenSeconds, kKeyframeInterval, kFramerate);
// One frame with a different resolution.
ProcessFrames(kFullHdWidth, kFullHdHeight,
/*is_hw_accelerated=*/false, 1, kKeyframeInterval, kFramerate);
// Verify that the counter are incremented.
EXPECT_GT(stats_callbacks_, last_stats_callbacks);
last_stats_callbacks = stats_callbacks_;
EXPECT_EQ(last_stats_key_.pixel_size, kHdWidth * kHdHeight);
EXPECT_GE(last_video_stats_.frame_count, 100);
EXPECT_LE(last_video_stats_.frame_count, kNumberOfFramesDuringTenSeconds);
EXPECT_NEAR(last_video_stats_.p99_processing_time_ms,
kExpectedP99ProcessingTimeMs, kP99ToleranceMs);
// Continue with new resolution and expect another report.
ProcessFrames(kFullHdWidth, kFullHdHeight, /*is_hw_accelerated=*/false,
kNumberOfFramesDuringTenSeconds, kKeyframeInterval, kFramerate);
EXPECT_GT(stats_callbacks_, last_stats_callbacks);
EXPECT_EQ(last_stats_key_.pixel_size, kFullHdWidth * kFullHdHeight);
EXPECT_GE(last_video_stats_.frame_count, 100);
EXPECT_LE(last_video_stats_.frame_count, kNumberOfFramesDuringTenSeconds);
EXPECT_NEAR(last_video_stats_.p99_processing_time_ms,
kExpectedP99ProcessingTimeMs, kP99ToleranceMs);
}
TEST_F(StatsCollectorTest, NewReportIfHwAccelerationChanges) {
constexpr int kNumberOfFramesDuringTenSeconds = kFramerate * 10;
EXPECT_EQ(stats_callbacks_, 0);
int last_stats_callbacks = stats_callbacks_;
ProcessFrames(kHdWidth, kHdHeight, /*is_hw_accelerated=*/false,
kNumberOfFramesDuringTenSeconds, kKeyframeInterval, kFramerate);
// One frame with HW acceleration.
ProcessFrames(kHdWidth, kHdHeight, /*is_hw_accelerated=*/true, 1,
kKeyframeInterval, kFramerate);
// Verify that the counter are incremented.
EXPECT_GT(stats_callbacks_, last_stats_callbacks);
last_stats_callbacks = stats_callbacks_;
EXPECT_EQ(last_stats_key_.pixel_size, kHdWidth * kHdHeight);
EXPECT_FALSE(last_stats_key_.hw_accelerated);
EXPECT_GE(last_video_stats_.frame_count, 100);
EXPECT_LE(last_video_stats_.frame_count, kNumberOfFramesDuringTenSeconds);
EXPECT_NEAR(last_video_stats_.p99_processing_time_ms,
kExpectedP99ProcessingTimeMs, kP99ToleranceMs);
// Continue with new resolution and expect another report.
ProcessFrames(kHdWidth, kHdHeight, /*is_hw_accelerated=*/true,
kNumberOfFramesDuringTenSeconds, kKeyframeInterval, kFramerate);
EXPECT_GT(stats_callbacks_, last_stats_callbacks);
EXPECT_EQ(last_stats_key_.pixel_size, kHdWidth * kHdHeight);
EXPECT_TRUE(last_stats_key_.hw_accelerated);
EXPECT_GE(last_video_stats_.frame_count, 100);
EXPECT_LE(last_video_stats_.frame_count, kNumberOfFramesDuringTenSeconds);
EXPECT_NEAR(last_video_stats_.p99_processing_time_ms,
kExpectedP99ProcessingTimeMs, kP99ToleranceMs);
}
TEST_F(StatsCollectorTest, NoCollectionAfter40000Frames) {
constexpr int kMinutesToRun = 10;
constexpr int kFrames = 40000;
EXPECT_EQ(stats_callbacks_, 0);
ProcessFrames(kHdWidth, kHdHeight, /*is_hw_accelerated=*/false, kFrames,
kKeyframeInterval, kFramerate);
EXPECT_GT(stats_callbacks_, 0);
int last_stats_callbacks = stats_callbacks_;
// Run for a few minutes and verify that no new callbacks are made.
for (int minute = 0; minute < kMinutesToRun; ++minute) {
ProcessFrames(kHdWidth, kHdHeight, /*is_hw_accelerated=*/false,
kFramesPerMinute, kKeyframeInterval, kFramerate);
// The expectation could be relaxed to allow for one callback to happen.
EXPECT_EQ(stats_callbacks_, last_stats_callbacks);
}
}
} // namespace
} // namespace blink