summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/test/pc/e2e/analyzer/video/analyzing_video_sink_test.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--third_party/libwebrtc/test/pc/e2e/analyzer/video/analyzing_video_sink_test.cc598
1 files changed, 598 insertions, 0 deletions
diff --git a/third_party/libwebrtc/test/pc/e2e/analyzer/video/analyzing_video_sink_test.cc b/third_party/libwebrtc/test/pc/e2e/analyzer/video/analyzing_video_sink_test.cc
new file mode 100644
index 0000000000..6cd89551ea
--- /dev/null
+++ b/third_party/libwebrtc/test/pc/e2e/analyzer/video/analyzing_video_sink_test.cc
@@ -0,0 +1,598 @@
+/*
+ * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "test/pc/e2e/analyzer/video/analyzing_video_sink.h"
+
+#include <stdio.h>
+
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "api/scoped_refptr.h"
+#include "api/test/create_frame_generator.h"
+#include "api/test/frame_generator_interface.h"
+#include "api/test/pclf/media_configuration.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "api/video/i420_buffer.h"
+#include "api/video/video_frame.h"
+#include "common_video/libyuv/include/webrtc_libyuv.h"
+#include "rtc_base/time_utils.h"
+#include "system_wrappers/include/clock.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/pc/e2e/analyzer/video/example_video_quality_analyzer.h"
+#include "test/testsupport/file_utils.h"
+#include "test/testsupport/frame_reader.h"
+#include "test/time_controller/simulated_time_controller.h"
+
+namespace webrtc {
+namespace webrtc_pc_e2e {
+namespace {
+
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::Ge;
+using ::testing::Test;
+
+// Remove files and directories in a directory non-recursively.
+void CleanDir(absl::string_view dir, size_t expected_output_files_count) {
+ absl::optional<std::vector<std::string>> dir_content =
+ test::ReadDirectory(dir);
+ if (expected_output_files_count == 0) {
+ ASSERT_TRUE(!dir_content.has_value() || dir_content->empty())
+ << "Empty directory is expected";
+ } else {
+ ASSERT_TRUE(dir_content.has_value()) << "Test directory is empty!";
+ EXPECT_EQ(dir_content->size(), expected_output_files_count);
+ for (const auto& entry : *dir_content) {
+ if (test::DirExists(entry)) {
+ EXPECT_TRUE(test::RemoveDir(entry))
+ << "Failed to remove sub directory: " << entry;
+ } else if (test::FileExists(entry)) {
+ EXPECT_TRUE(test::RemoveFile(entry))
+ << "Failed to remove file: " << entry;
+ } else {
+ FAIL() << "Can't remove unknown file type: " << entry;
+ }
+ }
+ }
+ EXPECT_TRUE(test::RemoveDir(dir)) << "Failed to remove directory: " << dir;
+}
+
+VideoFrame CreateFrame(test::FrameGeneratorInterface& frame_generator) {
+ test::FrameGeneratorInterface::VideoFrameData frame_data =
+ frame_generator.NextFrame();
+ return VideoFrame::Builder()
+ .set_video_frame_buffer(frame_data.buffer)
+ .set_update_rect(frame_data.update_rect)
+ .build();
+}
+
+std::unique_ptr<test::FrameGeneratorInterface> CreateFrameGenerator(
+ size_t width,
+ size_t height) {
+ return test::CreateSquareFrameGenerator(width, height,
+ /*type=*/absl::nullopt,
+ /*num_squares=*/absl::nullopt);
+}
+
+void AssertFrameIdsAre(const std::string& filename,
+ std::vector<std::string> expected_ids) {
+ FILE* file = fopen(filename.c_str(), "r");
+ ASSERT_TRUE(file != nullptr) << "Failed to open frame ids file: " << filename;
+ std::vector<std::string> actual_ids;
+ char buffer[8];
+ while (fgets(buffer, sizeof buffer, file) != nullptr) {
+ std::string current_id(buffer);
+ EXPECT_GE(current_id.size(), 2lu)
+ << "Found invalid frame id: [" << current_id << "]";
+ if (current_id.size() < 2) {
+ continue;
+ }
+ // Trim "\n" at the end.
+ actual_ids.push_back(current_id.substr(0, current_id.size() - 1));
+ }
+ fclose(file);
+ EXPECT_THAT(actual_ids, ElementsAreArray(expected_ids));
+}
+
+class AnalyzingVideoSinkTest : public Test {
+ protected:
+ ~AnalyzingVideoSinkTest() override = default;
+
+ void SetUp() override {
+ // Create an empty temporary directory for this test.
+ test_directory_ = test::JoinFilename(
+ test::OutputPath(),
+ "TestDir_AnalyzingVideoSinkTest_" +
+ std::string(
+ testing::UnitTest::GetInstance()->current_test_info()->name()));
+ test::CreateDir(test_directory_);
+ }
+
+ void TearDown() override {
+ CleanDir(test_directory_, expected_output_files_count_);
+ }
+
+ void ExpectOutputFilesCount(size_t count) {
+ expected_output_files_count_ = count;
+ }
+
+ std::string test_directory_;
+ size_t expected_output_files_count_ = 0;
+};
+
+TEST_F(AnalyzingVideoSinkTest, VideoFramesAreDumpedCorrectly) {
+ VideoSubscription subscription;
+ subscription.SubscribeToPeer(
+ "alice", VideoResolution(/*width=*/640, /*height=*/360, /*fps=*/30));
+ VideoConfig video_config("alice_video", /*width=*/1280, /*height=*/720,
+ /*fps=*/30);
+ video_config.output_dump_options = VideoDumpOptions(test_directory_);
+
+ ExampleVideoQualityAnalyzer analyzer;
+ std::unique_ptr<test::FrameGeneratorInterface> frame_generator =
+ CreateFrameGenerator(/*width=*/1280, /*height=*/720);
+ VideoFrame frame = CreateFrame(*frame_generator);
+ frame.set_id(analyzer.OnFrameCaptured("alice", "alice_video", frame));
+
+ {
+ // `helper` and `sink` has to be destroyed so all frames will be written
+ // to the disk.
+ AnalyzingVideoSinksHelper helper;
+ helper.AddConfig("alice", video_config);
+ AnalyzingVideoSink sink("bob", Clock::GetRealTimeClock(), analyzer, helper,
+ subscription, /*report_infra_stats=*/false);
+ sink.OnFrame(frame);
+ }
+
+ EXPECT_THAT(analyzer.frames_rendered(), Eq(static_cast<uint64_t>(1)));
+
+ auto frame_reader = test::CreateY4mFrameReader(
+ test::JoinFilename(test_directory_, "alice_video_bob_640x360_30.y4m"));
+ EXPECT_THAT(frame_reader->num_frames(), Eq(1));
+ rtc::scoped_refptr<I420Buffer> actual_frame = frame_reader->PullFrame();
+ rtc::scoped_refptr<I420BufferInterface> expected_frame =
+ frame.video_frame_buffer()->ToI420();
+ double psnr = I420PSNR(*expected_frame, *actual_frame);
+ double ssim = I420SSIM(*expected_frame, *actual_frame);
+ // Actual should be downscaled version of expected.
+ EXPECT_GT(ssim, 0.98);
+ EXPECT_GT(psnr, 38);
+
+ ExpectOutputFilesCount(1);
+}
+
+TEST_F(AnalyzingVideoSinkTest,
+ FallbackOnConfigResolutionIfNoSubscriptionProvided) {
+ VideoSubscription subscription;
+ VideoConfig video_config("alice_video", /*width=*/320, /*height=*/240,
+ /*fps=*/30);
+ video_config.output_dump_options = VideoDumpOptions(test_directory_);
+
+ ExampleVideoQualityAnalyzer analyzer;
+ std::unique_ptr<test::FrameGeneratorInterface> frame_generator =
+ CreateFrameGenerator(/*width=*/320, /*height=*/240);
+ VideoFrame frame = CreateFrame(*frame_generator);
+ frame.set_id(analyzer.OnFrameCaptured("alice", "alice_video", frame));
+
+ {
+ // `helper` and `sink` has to be destroyed so all frames will be written
+ // to the disk.
+ AnalyzingVideoSinksHelper helper;
+ helper.AddConfig("alice", video_config);
+ AnalyzingVideoSink sink("bob", Clock::GetRealTimeClock(), analyzer, helper,
+ subscription, /*report_infra_stats=*/false);
+ sink.OnFrame(frame);
+ }
+
+ EXPECT_THAT(analyzer.frames_rendered(), Eq(static_cast<uint64_t>(1)));
+
+ auto frame_reader = test::CreateY4mFrameReader(
+ test::JoinFilename(test_directory_, "alice_video_bob_320x240_30.y4m"));
+ EXPECT_THAT(frame_reader->num_frames(), Eq(1));
+ rtc::scoped_refptr<I420Buffer> actual_frame = frame_reader->PullFrame();
+ rtc::scoped_refptr<I420BufferInterface> expected_frame =
+ frame.video_frame_buffer()->ToI420();
+ double psnr = I420PSNR(*expected_frame, *actual_frame);
+ double ssim = I420SSIM(*expected_frame, *actual_frame);
+ // Frames should be equal.
+ EXPECT_DOUBLE_EQ(ssim, 1.00);
+ EXPECT_DOUBLE_EQ(psnr, 48);
+
+ ExpectOutputFilesCount(1);
+}
+
+TEST_F(AnalyzingVideoSinkTest,
+ FallbackOnConfigResolutionIfNoSubscriptionIsNotResolved) {
+ VideoSubscription subscription;
+ subscription.SubscribeToAllPeers(
+ VideoResolution(VideoResolution::Spec::kMaxFromSender));
+ VideoConfig video_config("alice_video", /*width=*/320, /*height=*/240,
+ /*fps=*/30);
+ video_config.output_dump_options = VideoDumpOptions(test_directory_);
+
+ ExampleVideoQualityAnalyzer analyzer;
+ std::unique_ptr<test::FrameGeneratorInterface> frame_generator =
+ CreateFrameGenerator(/*width=*/320, /*height=*/240);
+ VideoFrame frame = CreateFrame(*frame_generator);
+ frame.set_id(analyzer.OnFrameCaptured("alice", "alice_video", frame));
+
+ {
+ // `helper` and `sink` has to be destroyed so all frames will be written
+ // to the disk.
+ AnalyzingVideoSinksHelper helper;
+ helper.AddConfig("alice", video_config);
+ AnalyzingVideoSink sink("bob", Clock::GetRealTimeClock(), analyzer, helper,
+ subscription, /*report_infra_stats=*/false);
+ sink.OnFrame(frame);
+ }
+
+ EXPECT_THAT(analyzer.frames_rendered(), Eq(static_cast<uint64_t>(1)));
+
+ auto frame_reader = test::CreateY4mFrameReader(
+ test::JoinFilename(test_directory_, "alice_video_bob_320x240_30.y4m"));
+ EXPECT_THAT(frame_reader->num_frames(), Eq(1));
+ rtc::scoped_refptr<I420Buffer> actual_frame = frame_reader->PullFrame();
+ rtc::scoped_refptr<I420BufferInterface> expected_frame =
+ frame.video_frame_buffer()->ToI420();
+ double psnr = I420PSNR(*expected_frame, *actual_frame);
+ double ssim = I420SSIM(*expected_frame, *actual_frame);
+ // Frames should be equal.
+ EXPECT_DOUBLE_EQ(ssim, 1.00);
+ EXPECT_DOUBLE_EQ(psnr, 48);
+
+ ExpectOutputFilesCount(1);
+}
+
+TEST_F(AnalyzingVideoSinkTest,
+ VideoFramesAreDumpedCorrectlyWhenSubscriptionChanged) {
+ VideoSubscription subscription_before;
+ subscription_before.SubscribeToPeer(
+ "alice", VideoResolution(/*width=*/1280, /*height=*/720, /*fps=*/30));
+ VideoSubscription subscription_after;
+ subscription_after.SubscribeToPeer(
+ "alice", VideoResolution(/*width=*/640, /*height=*/360, /*fps=*/30));
+ VideoConfig video_config("alice_video", /*width=*/1280, /*height=*/720,
+ /*fps=*/30);
+ video_config.output_dump_options = VideoDumpOptions(test_directory_);
+
+ ExampleVideoQualityAnalyzer analyzer;
+ std::unique_ptr<test::FrameGeneratorInterface> frame_generator =
+ CreateFrameGenerator(/*width=*/1280, /*height=*/720);
+ VideoFrame frame_before = CreateFrame(*frame_generator);
+ frame_before.set_id(
+ analyzer.OnFrameCaptured("alice", "alice_video", frame_before));
+ VideoFrame frame_after = CreateFrame(*frame_generator);
+ frame_after.set_id(
+ analyzer.OnFrameCaptured("alice", "alice_video", frame_after));
+
+ {
+ // `helper` and `sink` has to be destroyed so all frames will be written
+ // to the disk.
+ AnalyzingVideoSinksHelper helper;
+ helper.AddConfig("alice", video_config);
+ AnalyzingVideoSink sink("bob", Clock::GetRealTimeClock(), analyzer, helper,
+ subscription_before, /*report_infra_stats=*/false);
+ sink.OnFrame(frame_before);
+
+ sink.UpdateSubscription(subscription_after);
+ sink.OnFrame(frame_after);
+ }
+
+ EXPECT_THAT(analyzer.frames_rendered(), Eq(static_cast<uint64_t>(2)));
+
+ {
+ auto frame_reader = test::CreateY4mFrameReader(
+ test::JoinFilename(test_directory_, "alice_video_bob_1280x720_30.y4m"));
+ EXPECT_THAT(frame_reader->num_frames(), Eq(1));
+ rtc::scoped_refptr<I420Buffer> actual_frame = frame_reader->PullFrame();
+ rtc::scoped_refptr<I420BufferInterface> expected_frame =
+ frame_before.video_frame_buffer()->ToI420();
+ double psnr = I420PSNR(*expected_frame, *actual_frame);
+ double ssim = I420SSIM(*expected_frame, *actual_frame);
+ // Frames should be equal.
+ EXPECT_DOUBLE_EQ(ssim, 1.00);
+ EXPECT_DOUBLE_EQ(psnr, 48);
+ }
+ {
+ auto frame_reader = test::CreateY4mFrameReader(
+ test::JoinFilename(test_directory_, "alice_video_bob_640x360_30.y4m"));
+ EXPECT_THAT(frame_reader->num_frames(), Eq(1));
+ rtc::scoped_refptr<I420Buffer> actual_frame = frame_reader->PullFrame();
+ rtc::scoped_refptr<I420BufferInterface> expected_frame =
+ frame_after.video_frame_buffer()->ToI420();
+ double psnr = I420PSNR(*expected_frame, *actual_frame);
+ double ssim = I420SSIM(*expected_frame, *actual_frame);
+ // Actual should be downscaled version of expected.
+ EXPECT_GT(ssim, 0.98);
+ EXPECT_GT(psnr, 38);
+ }
+
+ ExpectOutputFilesCount(2);
+}
+
+TEST_F(AnalyzingVideoSinkTest,
+ VideoFramesAreDumpedCorrectlyWhenSubscriptionChangedOnTheSameOne) {
+ VideoSubscription subscription_before;
+ subscription_before.SubscribeToPeer(
+ "alice", VideoResolution(/*width=*/640, /*height=*/360, /*fps=*/30));
+ VideoSubscription subscription_after;
+ subscription_after.SubscribeToPeer(
+ "alice", VideoResolution(/*width=*/640, /*height=*/360, /*fps=*/30));
+ VideoConfig video_config("alice_video", /*width=*/640, /*height=*/360,
+ /*fps=*/30);
+ video_config.output_dump_options = VideoDumpOptions(test_directory_);
+
+ ExampleVideoQualityAnalyzer analyzer;
+ std::unique_ptr<test::FrameGeneratorInterface> frame_generator =
+ CreateFrameGenerator(/*width=*/640, /*height=*/360);
+ VideoFrame frame_before = CreateFrame(*frame_generator);
+ frame_before.set_id(
+ analyzer.OnFrameCaptured("alice", "alice_video", frame_before));
+ VideoFrame frame_after = CreateFrame(*frame_generator);
+ frame_after.set_id(
+ analyzer.OnFrameCaptured("alice", "alice_video", frame_after));
+
+ {
+ // `helper` and `sink` has to be destroyed so all frames will be written
+ // to the disk.
+ AnalyzingVideoSinksHelper helper;
+ helper.AddConfig("alice", video_config);
+ AnalyzingVideoSink sink("bob", Clock::GetRealTimeClock(), analyzer, helper,
+ subscription_before, /*report_infra_stats=*/false);
+ sink.OnFrame(frame_before);
+
+ sink.UpdateSubscription(subscription_after);
+ sink.OnFrame(frame_after);
+ }
+
+ EXPECT_THAT(analyzer.frames_rendered(), Eq(static_cast<uint64_t>(2)));
+
+ {
+ auto frame_reader = test::CreateY4mFrameReader(
+ test::JoinFilename(test_directory_, "alice_video_bob_640x360_30.y4m"));
+ EXPECT_THAT(frame_reader->num_frames(), Eq(2));
+ // Read the first frame.
+ rtc::scoped_refptr<I420Buffer> actual_frame = frame_reader->PullFrame();
+ rtc::scoped_refptr<I420BufferInterface> expected_frame =
+ frame_before.video_frame_buffer()->ToI420();
+ // Frames should be equal.
+ EXPECT_DOUBLE_EQ(I420SSIM(*expected_frame, *actual_frame), 1.00);
+ EXPECT_DOUBLE_EQ(I420PSNR(*expected_frame, *actual_frame), 48);
+ // Read the second frame.
+ actual_frame = frame_reader->PullFrame();
+ expected_frame = frame_after.video_frame_buffer()->ToI420();
+ // Frames should be equal.
+ EXPECT_DOUBLE_EQ(I420SSIM(*expected_frame, *actual_frame), 1.00);
+ EXPECT_DOUBLE_EQ(I420PSNR(*expected_frame, *actual_frame), 48);
+ }
+
+ ExpectOutputFilesCount(1);
+}
+
+TEST_F(AnalyzingVideoSinkTest, SmallDiviationsInAspectRationAreAllowed) {
+ VideoSubscription subscription;
+ subscription.SubscribeToPeer(
+ "alice", VideoResolution(/*width=*/480, /*height=*/270, /*fps=*/30));
+ VideoConfig video_config("alice_video", /*width=*/480, /*height=*/270,
+ /*fps=*/30);
+ video_config.output_dump_options = VideoDumpOptions(test_directory_);
+
+ ExampleVideoQualityAnalyzer analyzer;
+ // Generator produces downscaled frames with a bit different aspect ration.
+ std::unique_ptr<test::FrameGeneratorInterface> frame_generator =
+ CreateFrameGenerator(/*width=*/240, /*height=*/136);
+ VideoFrame frame = CreateFrame(*frame_generator);
+ frame.set_id(analyzer.OnFrameCaptured("alice", "alice_video", frame));
+
+ {
+ // `helper` and `sink` has to be destroyed so all frames will be written
+ // to the disk.
+ AnalyzingVideoSinksHelper helper;
+ helper.AddConfig("alice", video_config);
+ AnalyzingVideoSink sink("bob", Clock::GetRealTimeClock(), analyzer, helper,
+ subscription, /*report_infra_stats=*/false);
+ sink.OnFrame(frame);
+ }
+
+ EXPECT_THAT(analyzer.frames_rendered(), Eq(static_cast<uint64_t>(1)));
+
+ {
+ auto frame_reader = test::CreateY4mFrameReader(
+ test::JoinFilename(test_directory_, "alice_video_bob_480x270_30.y4m"));
+ EXPECT_THAT(frame_reader->num_frames(), Eq(1));
+ // Read the first frame.
+ rtc::scoped_refptr<I420Buffer> actual_frame = frame_reader->PullFrame();
+ rtc::scoped_refptr<I420BufferInterface> expected_frame =
+ frame.video_frame_buffer()->ToI420();
+ // Actual frame is upscaled version of the expected. But because rendered
+ // resolution is equal to the actual frame size we need to upscale expected
+ // during comparison and then they have to be the same.
+ EXPECT_DOUBLE_EQ(I420SSIM(*actual_frame, *expected_frame), 1);
+ EXPECT_DOUBLE_EQ(I420PSNR(*actual_frame, *expected_frame), 48);
+ }
+
+ ExpectOutputFilesCount(1);
+}
+
+TEST_F(AnalyzingVideoSinkTest, VideoFramesIdsAreDumpedWhenRequested) {
+ VideoSubscription subscription;
+ subscription.SubscribeToPeer(
+ "alice", VideoResolution(/*width=*/320, /*height=*/240, /*fps=*/30));
+ VideoConfig video_config("alice_video", /*width=*/320, /*height=*/240,
+ /*fps=*/30);
+ video_config.output_dump_options =
+ VideoDumpOptions(test_directory_, /*export_frame_ids=*/true);
+
+ ExampleVideoQualityAnalyzer analyzer;
+ std::unique_ptr<test::FrameGeneratorInterface> frame_generator =
+ CreateFrameGenerator(/*width=*/320, /*height=*/240);
+
+ std::vector<std::string> expected_frame_ids;
+ {
+ // `helper` and `sink` has to be destroyed so all frames will be written
+ // to the disk.
+ AnalyzingVideoSinksHelper helper;
+ helper.AddConfig("alice", video_config);
+ AnalyzingVideoSink sink("bob", Clock::GetRealTimeClock(), analyzer, helper,
+ subscription, /*report_infra_stats=*/false);
+ for (int i = 0; i < 10; ++i) {
+ VideoFrame frame = CreateFrame(*frame_generator);
+ frame.set_id(analyzer.OnFrameCaptured("alice", "alice_video", frame));
+ expected_frame_ids.push_back(std::to_string(frame.id()));
+ sink.OnFrame(frame);
+ }
+ }
+
+ EXPECT_THAT(analyzer.frames_rendered(), Eq(static_cast<uint64_t>(10)));
+
+ AssertFrameIdsAre(
+ test::JoinFilename(test_directory_,
+ "alice_video_bob_320x240_30.frame_ids.txt"),
+ expected_frame_ids);
+
+ ExpectOutputFilesCount(2);
+}
+
+TEST_F(AnalyzingVideoSinkTest,
+ VideoFramesAndIdsAreDumpedWithFixedFpsWhenRequested) {
+ GlobalSimulatedTimeController simulated_time(Timestamp::Seconds(100000));
+
+ VideoSubscription subscription;
+ subscription.SubscribeToPeer(
+ "alice", VideoResolution(/*width=*/320, /*height=*/240, /*fps=*/10));
+ VideoConfig video_config("alice_video", /*width=*/320, /*height=*/240,
+ /*fps=*/10);
+ video_config.output_dump_options =
+ VideoDumpOptions(test_directory_, /*export_frame_ids=*/true);
+ video_config.output_dump_use_fixed_framerate = true;
+
+ ExampleVideoQualityAnalyzer analyzer;
+ std::unique_ptr<test::FrameGeneratorInterface> frame_generator =
+ CreateFrameGenerator(/*width=*/320, /*height=*/240);
+
+ VideoFrame frame1 = CreateFrame(*frame_generator);
+ frame1.set_id(analyzer.OnFrameCaptured("alice", "alice_video", frame1));
+ VideoFrame frame2 = CreateFrame(*frame_generator);
+ frame2.set_id(analyzer.OnFrameCaptured("alice", "alice_video", frame2));
+
+ {
+ // `helper` and `sink` has to be destroyed so all frames will be written
+ // to the disk.
+ AnalyzingVideoSinksHelper helper;
+ helper.AddConfig("alice", video_config);
+ AnalyzingVideoSink sink("bob", simulated_time.GetClock(), analyzer, helper,
+ subscription, /*report_infra_stats=*/false);
+ sink.OnFrame(frame1);
+ // Advance almost 1 second, so the first frame has to be repeated 9 time
+ // more.
+ simulated_time.AdvanceTime(TimeDelta::Millis(990));
+ sink.OnFrame(frame2);
+ simulated_time.AdvanceTime(TimeDelta::Millis(100));
+ }
+
+ EXPECT_THAT(analyzer.frames_rendered(), Eq(static_cast<uint64_t>(2)));
+
+ auto frame_reader = test::CreateY4mFrameReader(
+ test::JoinFilename(test_directory_, "alice_video_bob_320x240_10.y4m"));
+ EXPECT_THAT(frame_reader->num_frames(), Eq(11));
+ for (int i = 0; i < 10; ++i) {
+ rtc::scoped_refptr<I420Buffer> actual_frame = frame_reader->PullFrame();
+ rtc::scoped_refptr<I420BufferInterface> expected_frame =
+ frame1.video_frame_buffer()->ToI420();
+ double psnr = I420PSNR(*expected_frame, *actual_frame);
+ double ssim = I420SSIM(*expected_frame, *actual_frame);
+ // Frames should be equal.
+ EXPECT_DOUBLE_EQ(ssim, 1.00);
+ EXPECT_DOUBLE_EQ(psnr, 48);
+ }
+ rtc::scoped_refptr<I420Buffer> actual_frame = frame_reader->PullFrame();
+ rtc::scoped_refptr<I420BufferInterface> expected_frame =
+ frame2.video_frame_buffer()->ToI420();
+ double psnr = I420PSNR(*expected_frame, *actual_frame);
+ double ssim = I420SSIM(*expected_frame, *actual_frame);
+ // Frames should be equal.
+ EXPECT_DOUBLE_EQ(ssim, 1.00);
+ EXPECT_DOUBLE_EQ(psnr, 48);
+
+ AssertFrameIdsAre(
+ test::JoinFilename(test_directory_,
+ "alice_video_bob_320x240_10.frame_ids.txt"),
+ {std::to_string(frame1.id()), std::to_string(frame1.id()),
+ std::to_string(frame1.id()), std::to_string(frame1.id()),
+ std::to_string(frame1.id()), std::to_string(frame1.id()),
+ std::to_string(frame1.id()), std::to_string(frame1.id()),
+ std::to_string(frame1.id()), std::to_string(frame1.id()),
+ std::to_string(frame2.id())});
+
+ ExpectOutputFilesCount(2);
+}
+
+TEST_F(AnalyzingVideoSinkTest, InfraMetricsCollectedWhenRequested) {
+ VideoSubscription subscription;
+ subscription.SubscribeToPeer(
+ "alice", VideoResolution(/*width=*/1280, /*height=*/720, /*fps=*/30));
+ VideoConfig video_config("alice_video", /*width=*/640, /*height=*/360,
+ /*fps=*/30);
+
+ ExampleVideoQualityAnalyzer analyzer;
+ std::unique_ptr<test::FrameGeneratorInterface> frame_generator =
+ CreateFrameGenerator(/*width=*/640, /*height=*/360);
+ VideoFrame frame = CreateFrame(*frame_generator);
+ frame.set_id(analyzer.OnFrameCaptured("alice", "alice_video", frame));
+
+ AnalyzingVideoSinksHelper helper;
+ helper.AddConfig("alice", video_config);
+ AnalyzingVideoSink sink("bob", Clock::GetRealTimeClock(), analyzer, helper,
+ subscription, /*report_infra_stats=*/true);
+ sink.OnFrame(frame);
+
+ AnalyzingVideoSink::Stats stats = sink.stats();
+ EXPECT_THAT(stats.scaling_tims_ms.NumSamples(), Eq(1));
+ EXPECT_THAT(stats.scaling_tims_ms.GetAverage(), Ge(0));
+ EXPECT_THAT(stats.analyzing_sink_processing_time_ms.NumSamples(), Eq(1));
+ EXPECT_THAT(stats.analyzing_sink_processing_time_ms.GetAverage(),
+ Ge(stats.scaling_tims_ms.GetAverage()));
+
+ ExpectOutputFilesCount(0);
+}
+
+TEST_F(AnalyzingVideoSinkTest, InfraMetricsNotCollectedWhenNotRequested) {
+ VideoSubscription subscription;
+ subscription.SubscribeToPeer(
+ "alice", VideoResolution(/*width=*/1280, /*height=*/720, /*fps=*/30));
+ VideoConfig video_config("alice_video", /*width=*/640, /*height=*/360,
+ /*fps=*/30);
+
+ ExampleVideoQualityAnalyzer analyzer;
+ std::unique_ptr<test::FrameGeneratorInterface> frame_generator =
+ CreateFrameGenerator(/*width=*/640, /*height=*/360);
+ VideoFrame frame = CreateFrame(*frame_generator);
+ frame.set_id(analyzer.OnFrameCaptured("alice", "alice_video", frame));
+
+ AnalyzingVideoSinksHelper helper;
+ helper.AddConfig("alice", video_config);
+ AnalyzingVideoSink sink("bob", Clock::GetRealTimeClock(), analyzer, helper,
+ subscription, /*report_infra_stats=*/false);
+ sink.OnFrame(frame);
+
+ AnalyzingVideoSink::Stats stats = sink.stats();
+ EXPECT_THAT(stats.scaling_tims_ms.NumSamples(), Eq(0));
+ EXPECT_THAT(stats.analyzing_sink_processing_time_ms.NumSamples(), Eq(0));
+
+ ExpectOutputFilesCount(0);
+}
+
+} // namespace
+} // namespace webrtc_pc_e2e
+} // namespace webrtc