[PCLF] Extract video dumping from video_quality_analyzer_injection_helper for testability
Bug: b/240540204 Change-Id: I7f5970fae2b1472b37ea5fd5cbb16b2ce25dd968 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/278622 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Commit-Queue: Artem Titov <titovartem@webrtc.org> Cr-Commit-Position: refs/heads/main@{#38347}
This commit is contained in:
parent
a97dc0579c
commit
6b75058774
@ -44,6 +44,7 @@ if (!build_with_chromium) {
|
||||
":peer_connection_quality_test_metric_names_test",
|
||||
":single_process_encoded_image_data_injector_unittest",
|
||||
":stats_poller_test",
|
||||
":video_dumping_test",
|
||||
":video_frame_tracking_id_injector_unittest",
|
||||
]
|
||||
}
|
||||
@ -177,6 +178,40 @@ if (!build_with_chromium) {
|
||||
}
|
||||
|
||||
if (rtc_include_tests) {
|
||||
rtc_library("video_dumping") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"analyzer/video/video_dumping.cc",
|
||||
"analyzer/video/video_dumping.h",
|
||||
]
|
||||
deps = [
|
||||
"../..:video_test_support",
|
||||
"../../../api:peer_connection_quality_test_fixture_api",
|
||||
"../../../api/video:video_frame",
|
||||
"../../../system_wrappers",
|
||||
]
|
||||
absl_deps = [
|
||||
"//third_party/abseil-cpp/absl/strings",
|
||||
"//third_party/abseil-cpp/absl/types:optional",
|
||||
]
|
||||
}
|
||||
|
||||
rtc_library("video_dumping_test") {
|
||||
testonly = true
|
||||
sources = [ "analyzer/video/video_dumping_test.cc" ]
|
||||
deps = [
|
||||
":video_dumping",
|
||||
"../..:fileutils",
|
||||
"../..:test_support",
|
||||
"../..:video_test_support",
|
||||
"../../../api:peer_connection_quality_test_fixture_api",
|
||||
"../../../api:scoped_refptr",
|
||||
"../../../api/video:video_frame",
|
||||
"../../../rtc_base:random",
|
||||
]
|
||||
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
|
||||
}
|
||||
|
||||
rtc_library("video_quality_analyzer_injection_helper") {
|
||||
visibility = [ "*" ]
|
||||
testonly = true
|
||||
@ -189,6 +224,7 @@ if (!build_with_chromium) {
|
||||
":quality_analyzing_video_decoder",
|
||||
":quality_analyzing_video_encoder",
|
||||
":simulcast_dummy_buffer_helper",
|
||||
":video_dumping",
|
||||
"../..:fixed_fps_video_frame_writer_adapter",
|
||||
"../..:test_renderer",
|
||||
"../../../api:array_view",
|
||||
|
||||
121
test/pc/e2e/analyzer/video/video_dumping.cc
Normal file
121
test/pc/e2e/analyzer/video/video_dumping.cc
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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/video_dumping.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/test/peerconnection_quality_test_fixture.h"
|
||||
#include "api/video/video_frame.h"
|
||||
#include "system_wrappers/include/clock.h"
|
||||
#include "test/testsupport/video_frame_writer.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace webrtc_pc_e2e {
|
||||
namespace {
|
||||
|
||||
class VideoFrameIdsWriter final : public test::VideoFrameWriter {
|
||||
public:
|
||||
explicit VideoFrameIdsWriter(absl::string_view file_name)
|
||||
: file_name_(file_name) {
|
||||
output_file_ = fopen(file_name_.c_str(), "wb");
|
||||
RTC_CHECK(output_file_ != nullptr)
|
||||
<< "Failed to open file to dump frame ids for writing: " << file_name_;
|
||||
}
|
||||
~VideoFrameIdsWriter() override { Close(); }
|
||||
|
||||
bool WriteFrame(const VideoFrame& frame) override {
|
||||
RTC_CHECK(output_file_ != nullptr) << "Writer is already closed";
|
||||
int chars_written = fprintf(output_file_, "%d\n", frame.id());
|
||||
if (chars_written < 2) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to write frame id to the output file: "
|
||||
<< file_name_;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Close() override {
|
||||
if (output_file_ != nullptr) {
|
||||
fclose(output_file_);
|
||||
output_file_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string file_name_;
|
||||
FILE* output_file_;
|
||||
};
|
||||
|
||||
// Broadcast received frame to multiple underlying frame writers.
|
||||
class BroadcastingFrameWriter final : public test::VideoFrameWriter {
|
||||
public:
|
||||
explicit BroadcastingFrameWriter(
|
||||
std::vector<std::unique_ptr<test::VideoFrameWriter>> delegates)
|
||||
: delegates_(std::move(delegates)) {}
|
||||
~BroadcastingFrameWriter() override { Close(); }
|
||||
|
||||
bool WriteFrame(const webrtc::VideoFrame& frame) override {
|
||||
for (auto& delegate : delegates_) {
|
||||
if (!delegate->WriteFrame(frame)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Close() override {
|
||||
for (auto& delegate : delegates_) {
|
||||
delegate->Close();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<test::VideoFrameWriter>> delegates_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
VideoWriter::VideoWriter(test::VideoFrameWriter* video_writer,
|
||||
int sampling_modulo)
|
||||
: video_writer_(video_writer), sampling_modulo_(sampling_modulo) {}
|
||||
|
||||
void VideoWriter::OnFrame(const VideoFrame& frame) {
|
||||
if (frames_counter_++ % sampling_modulo_ != 0) {
|
||||
return;
|
||||
}
|
||||
bool result = video_writer_->WriteFrame(frame);
|
||||
RTC_CHECK(result) << "Failed to write frame";
|
||||
}
|
||||
|
||||
std::unique_ptr<test::VideoFrameWriter> CreateVideoFrameWriter(
|
||||
absl::string_view file_name,
|
||||
absl::optional<std::string> frame_ids_dump_file_name,
|
||||
const PeerConnectionE2EQualityTestFixture::VideoResolution& resolution) {
|
||||
std::vector<std::unique_ptr<test::VideoFrameWriter>> requested_writers;
|
||||
requested_writers.push_back(std::make_unique<test::Y4mVideoFrameWriterImpl>(
|
||||
std::string(file_name), resolution.width(), resolution.height(),
|
||||
resolution.fps()));
|
||||
if (frame_ids_dump_file_name.has_value()) {
|
||||
requested_writers.push_back(
|
||||
std::make_unique<VideoFrameIdsWriter>(*frame_ids_dump_file_name));
|
||||
}
|
||||
return std::make_unique<BroadcastingFrameWriter>(
|
||||
std::move(requested_writers));
|
||||
}
|
||||
|
||||
} // namespace webrtc_pc_e2e
|
||||
} // namespace webrtc
|
||||
58
test/pc/e2e/analyzer/video/video_dumping.h
Normal file
58
test/pc/e2e/analyzer/video/video_dumping.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TEST_PC_E2E_ANALYZER_VIDEO_VIDEO_DUMPING_H_
|
||||
#define TEST_PC_E2E_ANALYZER_VIDEO_VIDEO_DUMPING_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/test/peerconnection_quality_test_fixture.h"
|
||||
#include "api/video/video_frame.h"
|
||||
#include "api/video/video_sink_interface.h"
|
||||
#include "test/testsupport/video_frame_writer.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace webrtc_pc_e2e {
|
||||
|
||||
// `VideoSinkInterface` to dump incoming video frames into specified video
|
||||
// writer.
|
||||
class VideoWriter final : public rtc::VideoSinkInterface<VideoFrame> {
|
||||
public:
|
||||
// Creates video writer. Caller keeps ownership of `video_writer` and is
|
||||
// responsible for closing it after VideoWriter will be destroyed.
|
||||
VideoWriter(test::VideoFrameWriter* video_writer, int sampling_modulo);
|
||||
VideoWriter(const VideoWriter&) = delete;
|
||||
VideoWriter& operator=(const VideoWriter&) = delete;
|
||||
~VideoWriter() override = default;
|
||||
|
||||
void OnFrame(const VideoFrame& frame) override;
|
||||
|
||||
private:
|
||||
test::VideoFrameWriter* const video_writer_;
|
||||
const int sampling_modulo_;
|
||||
|
||||
int64_t frames_counter_ = 0;
|
||||
};
|
||||
|
||||
// Creates a `VideoFrameWriter` to dump video frames into the specified file
|
||||
// `file_name`. If `frame_ids_dump_file_name` is specified then created writer
|
||||
// will also log ids of those frames into the specified file.
|
||||
std::unique_ptr<test::VideoFrameWriter> CreateVideoFrameWriter(
|
||||
absl::string_view file_name,
|
||||
absl::optional<std::string> frame_ids_dump_file_name,
|
||||
const PeerConnectionE2EQualityTestFixture::VideoResolution& resolution);
|
||||
|
||||
} // namespace webrtc_pc_e2e
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // TEST_PC_E2E_ANALYZER_VIDEO_VIDEO_DUMPING_H_
|
||||
227
test/pc/e2e/analyzer/video/video_dumping_test.cc
Normal file
227
test/pc/e2e/analyzer/video/video_dumping_test.cc
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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/video_dumping.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/scoped_refptr.h"
|
||||
#include "api/test/peerconnection_quality_test_fixture.h"
|
||||
#include "api/video/i420_buffer.h"
|
||||
#include "api/video/video_frame.h"
|
||||
#include "api/video/video_frame_buffer.h"
|
||||
#include "rtc_base/random.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/testsupport/file_utils.h"
|
||||
#include "test/testsupport/frame_reader.h"
|
||||
#include "test/testsupport/video_frame_writer.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace webrtc_pc_e2e {
|
||||
namespace {
|
||||
|
||||
using ::testing::ElementsAreArray;
|
||||
using ::testing::Eq;
|
||||
using ::testing::Test;
|
||||
|
||||
using VideoResolution = ::webrtc::webrtc_pc_e2e::
|
||||
PeerConnectionE2EQualityTestFixture::VideoResolution;
|
||||
|
||||
uint8_t RandByte(Random& random) {
|
||||
return random.Rand(255);
|
||||
}
|
||||
|
||||
VideoFrame CreateRandom2x2VideoFrame(uint16_t id, Random& random) {
|
||||
rtc::scoped_refptr<I420Buffer> buffer = I420Buffer::Create(2, 2);
|
||||
|
||||
uint8_t data[6] = {RandByte(random), RandByte(random), RandByte(random),
|
||||
RandByte(random), RandByte(random), RandByte(random)};
|
||||
|
||||
memcpy(buffer->MutableDataY(), data, 2);
|
||||
memcpy(buffer->MutableDataY() + buffer->StrideY(), data + 2, 2);
|
||||
memcpy(buffer->MutableDataU(), data + 4, 1);
|
||||
memcpy(buffer->MutableDataV(), data + 5, 1);
|
||||
|
||||
return VideoFrame::Builder()
|
||||
.set_id(id)
|
||||
.set_video_frame_buffer(buffer)
|
||||
.set_timestamp_us(1)
|
||||
.build();
|
||||
}
|
||||
|
||||
std::vector<uint8_t> AsVector(const uint8_t* data, size_t size) {
|
||||
std::vector<uint8_t> out;
|
||||
out.assign(data, data + size);
|
||||
return out;
|
||||
}
|
||||
|
||||
void AssertFramesEqual(rtc::scoped_refptr<webrtc::I420BufferInterface> actual,
|
||||
rtc::scoped_refptr<VideoFrameBuffer> expected) {
|
||||
ASSERT_THAT(actual->width(), Eq(expected->width()));
|
||||
ASSERT_THAT(actual->height(), Eq(expected->height()));
|
||||
rtc::scoped_refptr<webrtc::I420BufferInterface> expected_i420 =
|
||||
expected->ToI420();
|
||||
|
||||
int height = actual->height();
|
||||
|
||||
EXPECT_THAT(AsVector(actual->DataY(), actual->StrideY() * height),
|
||||
ElementsAreArray(expected_i420->DataY(),
|
||||
expected_i420->StrideY() * height));
|
||||
EXPECT_THAT(AsVector(actual->DataU(), actual->StrideU() * (height + 1) / 2),
|
||||
ElementsAreArray(expected_i420->DataU(),
|
||||
expected_i420->StrideU() * (height + 1) / 2));
|
||||
EXPECT_THAT(AsVector(actual->DataV(), actual->StrideV() * (height + 1) / 2),
|
||||
ElementsAreArray(expected_i420->DataV(),
|
||||
expected_i420->StrideV() * (height + 1) / 2));
|
||||
}
|
||||
|
||||
void AssertFrameIdsAre(const std::string& filename,
|
||||
std::vector<std::string> expected_ids) {
|
||||
FILE* file = fopen(filename.c_str(), "r");
|
||||
ASSERT_TRUE(file != nullptr);
|
||||
std::vector<std::string> actual_ids;
|
||||
char buffer[8];
|
||||
while (fgets(buffer, sizeof buffer, file) != nullptr) {
|
||||
std::string current_id(buffer);
|
||||
ASSERT_GE(current_id.size(), 2lu);
|
||||
// Trim "\n" at the end.
|
||||
actual_ids.push_back(current_id.substr(0, current_id.size() - 1));
|
||||
}
|
||||
EXPECT_THAT(actual_ids, ElementsAreArray(expected_ids));
|
||||
}
|
||||
|
||||
class VideoDumpingTest : public Test {
|
||||
protected:
|
||||
~VideoDumpingTest() override = default;
|
||||
|
||||
void SetUp() override {
|
||||
video_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
|
||||
"video_dumping_test");
|
||||
ids_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
|
||||
"video_dumping_test");
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
remove(video_filename_.c_str());
|
||||
remove(ids_filename_.c_str());
|
||||
}
|
||||
|
||||
std::string video_filename_;
|
||||
std::string ids_filename_;
|
||||
};
|
||||
|
||||
using CreateVideoFrameWriterTest = VideoDumpingTest;
|
||||
|
||||
TEST_F(CreateVideoFrameWriterTest, VideoIsWritenCorrectly) {
|
||||
Random random(/*seed=*/100);
|
||||
VideoFrame frame1 = CreateRandom2x2VideoFrame(1, random);
|
||||
VideoFrame frame2 = CreateRandom2x2VideoFrame(2, random);
|
||||
|
||||
std::unique_ptr<test::VideoFrameWriter> writer = CreateVideoFrameWriter(
|
||||
video_filename_,
|
||||
/*frame_ids_dump_file_name=*/absl::nullopt,
|
||||
VideoResolution(/*width=*/2, /*height=*/2, /*fps=*/2));
|
||||
|
||||
ASSERT_TRUE(writer->WriteFrame(frame1));
|
||||
ASSERT_TRUE(writer->WriteFrame(frame2));
|
||||
writer->Close();
|
||||
|
||||
test::Y4mFrameReaderImpl frame_reader(video_filename_, /*width=*/2,
|
||||
/*height=*/2);
|
||||
ASSERT_TRUE(frame_reader.Init());
|
||||
EXPECT_THAT(frame_reader.NumberOfFrames(), Eq(2));
|
||||
AssertFramesEqual(frame_reader.ReadFrame(), frame1.video_frame_buffer());
|
||||
AssertFramesEqual(frame_reader.ReadFrame(), frame2.video_frame_buffer());
|
||||
}
|
||||
|
||||
TEST_F(CreateVideoFrameWriterTest, VideoIsWritenWithFrameIds) {
|
||||
Random random(/*seed=*/100);
|
||||
VideoFrame frame1 = CreateRandom2x2VideoFrame(1, random);
|
||||
VideoFrame frame2 = CreateRandom2x2VideoFrame(2, random);
|
||||
|
||||
std::unique_ptr<test::VideoFrameWriter> writer = CreateVideoFrameWriter(
|
||||
video_filename_, ids_filename_,
|
||||
VideoResolution(/*width=*/2, /*height=*/2, /*fps=*/2));
|
||||
|
||||
ASSERT_TRUE(writer->WriteFrame(frame1));
|
||||
ASSERT_TRUE(writer->WriteFrame(frame2));
|
||||
writer->Close();
|
||||
|
||||
test::Y4mFrameReaderImpl frame_reader(video_filename_, /*width=*/2,
|
||||
/*height=*/2);
|
||||
ASSERT_TRUE(frame_reader.Init());
|
||||
EXPECT_THAT(frame_reader.NumberOfFrames(), Eq(2));
|
||||
AssertFramesEqual(frame_reader.ReadFrame(), frame1.video_frame_buffer());
|
||||
AssertFramesEqual(frame_reader.ReadFrame(), frame2.video_frame_buffer());
|
||||
AssertFrameIdsAre(ids_filename_, {"1", "2"});
|
||||
}
|
||||
|
||||
using VideoWriterTest = VideoDumpingTest;
|
||||
|
||||
TEST_F(VideoWriterTest, AllFramesAreWrittenWithSamplingModulo1) {
|
||||
Random random(/*seed=*/100);
|
||||
VideoFrame frame1 = CreateRandom2x2VideoFrame(1, random);
|
||||
VideoFrame frame2 = CreateRandom2x2VideoFrame(2, random);
|
||||
|
||||
{
|
||||
std::unique_ptr<test::VideoFrameWriter> frame_writer =
|
||||
CreateVideoFrameWriter(
|
||||
video_filename_, /*frame_ids_dump_file_name=*/absl::nullopt,
|
||||
VideoResolution(/*width=*/2, /*height=*/2, /*fps=*/2));
|
||||
VideoWriter writer(frame_writer.get(), /*sampling_modulo=*/1);
|
||||
|
||||
writer.OnFrame(frame1);
|
||||
writer.OnFrame(frame2);
|
||||
frame_writer->Close();
|
||||
}
|
||||
|
||||
test::Y4mFrameReaderImpl frame_reader(video_filename_, /*width=*/2,
|
||||
/*height=*/2);
|
||||
ASSERT_TRUE(frame_reader.Init());
|
||||
EXPECT_THAT(frame_reader.NumberOfFrames(), Eq(2));
|
||||
AssertFramesEqual(frame_reader.ReadFrame(), frame1.video_frame_buffer());
|
||||
AssertFramesEqual(frame_reader.ReadFrame(), frame2.video_frame_buffer());
|
||||
}
|
||||
|
||||
TEST_F(VideoWriterTest, OnlyEvery2ndFramesIsWrittenWithSamplingModulo2) {
|
||||
Random random(/*seed=*/100);
|
||||
VideoFrame frame1 = CreateRandom2x2VideoFrame(1, random);
|
||||
VideoFrame frame2 = CreateRandom2x2VideoFrame(2, random);
|
||||
VideoFrame frame3 = CreateRandom2x2VideoFrame(3, random);
|
||||
|
||||
{
|
||||
std::unique_ptr<test::VideoFrameWriter> frame_writer =
|
||||
CreateVideoFrameWriter(
|
||||
video_filename_, /*frame_ids_dump_file_name=*/absl::nullopt,
|
||||
VideoResolution(/*width=*/2, /*height=*/2, /*fps=*/2));
|
||||
VideoWriter writer(frame_writer.get(), /*sampling_modulo=*/2);
|
||||
|
||||
writer.OnFrame(frame1);
|
||||
writer.OnFrame(frame2);
|
||||
writer.OnFrame(frame3);
|
||||
frame_writer->Close();
|
||||
}
|
||||
|
||||
test::Y4mFrameReaderImpl frame_reader(video_filename_, /*width=*/2,
|
||||
/*height=*/2);
|
||||
ASSERT_TRUE(frame_reader.Init());
|
||||
EXPECT_THAT(frame_reader.NumberOfFrames(), Eq(2));
|
||||
AssertFramesEqual(frame_reader.ReadFrame(), frame1.video_frame_buffer());
|
||||
AssertFramesEqual(frame_reader.ReadFrame(), frame3.video_frame_buffer());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace webrtc_pc_e2e
|
||||
} // namespace webrtc
|
||||
@ -26,6 +26,7 @@
|
||||
#include "test/pc/e2e/analyzer/video/quality_analyzing_video_decoder.h"
|
||||
#include "test/pc/e2e/analyzer/video/quality_analyzing_video_encoder.h"
|
||||
#include "test/pc/e2e/analyzer/video/simulcast_dummy_buffer_helper.h"
|
||||
#include "test/pc/e2e/analyzer/video/video_dumping.h"
|
||||
#include "test/testsupport/fixed_fps_video_frame_writer_adapter.h"
|
||||
#include "test/video_renderer.h"
|
||||
|
||||
@ -71,76 +72,8 @@ class AnalyzingFramePreprocessor
|
||||
sinks_;
|
||||
};
|
||||
|
||||
// Broadcast received frame to multiple underlying frame writers.
|
||||
class BroadcastingFrameWriter final : public test::VideoFrameWriter {
|
||||
public:
|
||||
explicit BroadcastingFrameWriter(
|
||||
std::vector<std::unique_ptr<test::VideoFrameWriter>> delegates)
|
||||
: delegates_(std::move(delegates)) {}
|
||||
~BroadcastingFrameWriter() override { Close(); }
|
||||
|
||||
bool WriteFrame(const webrtc::VideoFrame& frame) override {
|
||||
for (auto& delegate : delegates_) {
|
||||
if (!delegate->WriteFrame(frame)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Close() override {
|
||||
for (auto& delegate : delegates_) {
|
||||
delegate->Close();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<test::VideoFrameWriter>> delegates_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
VideoQualityAnalyzerInjectionHelper::VideoFrameIdsWriter::VideoFrameIdsWriter(
|
||||
absl::string_view file_name)
|
||||
: file_name_(file_name) {
|
||||
output_file_ = fopen(file_name_.c_str(), "wb");
|
||||
RTC_CHECK(output_file_ != nullptr)
|
||||
<< "Failed to open file to dump frame ids for writing: " << file_name_;
|
||||
}
|
||||
|
||||
bool VideoQualityAnalyzerInjectionHelper::VideoFrameIdsWriter::WriteFrame(
|
||||
const VideoFrame& frame) {
|
||||
RTC_CHECK(output_file_ != nullptr) << "Writer is already closed";
|
||||
int chars_written = fprintf(output_file_, "%d\n", frame.id());
|
||||
if (chars_written < 2) {
|
||||
RTC_LOG(LS_ERROR) << "Failed to write frame id to the output file: "
|
||||
<< file_name_;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void VideoQualityAnalyzerInjectionHelper::VideoFrameIdsWriter::Close() {
|
||||
if (output_file_ != nullptr) {
|
||||
fclose(output_file_);
|
||||
output_file_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
VideoQualityAnalyzerInjectionHelper::VideoWriter::VideoWriter(
|
||||
test::VideoFrameWriter* video_writer,
|
||||
int sampling_modulo)
|
||||
: video_writer_(video_writer), sampling_modulo_(sampling_modulo) {}
|
||||
|
||||
void VideoQualityAnalyzerInjectionHelper::VideoWriter::OnFrame(
|
||||
const VideoFrame& frame) {
|
||||
if (frames_counter_++ % sampling_modulo_ != 0) {
|
||||
return;
|
||||
}
|
||||
bool result = video_writer_->WriteFrame(frame);
|
||||
RTC_CHECK(result) << "Failed to write frame";
|
||||
}
|
||||
|
||||
VideoQualityAnalyzerInjectionHelper::VideoQualityAnalyzerInjectionHelper(
|
||||
Clock* clock,
|
||||
std::unique_ptr<VideoQualityAnalyzerInterface> analyzer,
|
||||
@ -181,15 +114,15 @@ VideoQualityAnalyzerInjectionHelper::CreateFramePreprocessor(
|
||||
absl::string_view peer_name,
|
||||
const VideoConfig& config) {
|
||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
|
||||
test::VideoFrameWriter* writer = nullptr;
|
||||
if (config.input_dump_options.has_value()) {
|
||||
writer = CreateVideoWriter(
|
||||
std::unique_ptr<test::VideoFrameWriter> writer = CreateVideoFrameWriter(
|
||||
config.input_dump_options->GetInputDumpFileName(*config.stream_label),
|
||||
config.input_dump_options->GetInputFrameIdsDumpFileName(
|
||||
*config.stream_label),
|
||||
config);
|
||||
config.GetResolution());
|
||||
sinks.push_back(std::make_unique<VideoWriter>(
|
||||
writer, config.input_dump_options->sampling_modulo()));
|
||||
writer.get(), config.input_dump_options->sampling_modulo()));
|
||||
video_writers_.push_back(std::move(writer));
|
||||
}
|
||||
if (config.show_on_screen) {
|
||||
sinks.push_back(absl::WrapUnique(
|
||||
@ -251,31 +184,6 @@ void VideoQualityAnalyzerInjectionHelper::Stop() {
|
||||
video_writers_.clear();
|
||||
}
|
||||
|
||||
test::VideoFrameWriter* VideoQualityAnalyzerInjectionHelper::CreateVideoWriter(
|
||||
absl::string_view file_name,
|
||||
absl::optional<std::string> frame_ids_dump_file_name,
|
||||
const PeerConnectionE2EQualityTestFixture::VideoConfig& config) {
|
||||
// TODO(titovartem) create only one file writer for simulcast video track.
|
||||
// For now this code will be invoked for each simulcast stream separately, but
|
||||
// only one file will be used.
|
||||
std::vector<std::unique_ptr<test::VideoFrameWriter>> requested_writers;
|
||||
requested_writers.push_back(std::make_unique<test::Y4mVideoFrameWriterImpl>(
|
||||
std::string(file_name), config.width, config.height, config.fps));
|
||||
if (frame_ids_dump_file_name.has_value()) {
|
||||
requested_writers.push_back(
|
||||
std::make_unique<VideoFrameIdsWriter>(*frame_ids_dump_file_name));
|
||||
}
|
||||
std::unique_ptr<test::VideoFrameWriter> video_writer =
|
||||
std::make_unique<BroadcastingFrameWriter>(std::move(requested_writers));
|
||||
if (config.output_dump_use_fixed_framerate) {
|
||||
video_writer = std::make_unique<test::FixedFpsVideoFrameWriterAdapter>(
|
||||
config.fps, clock_, std::move(video_writer));
|
||||
}
|
||||
test::VideoFrameWriter* out = video_writer.get();
|
||||
video_writers_.push_back(std::move(video_writer));
|
||||
return out;
|
||||
}
|
||||
|
||||
void VideoQualityAnalyzerInjectionHelper::OnFrame(absl::string_view peer_name,
|
||||
const VideoFrame& frame) {
|
||||
rtc::scoped_refptr<I420BufferInterface> i420_buffer =
|
||||
@ -317,16 +225,20 @@ VideoQualityAnalyzerInjectionHelper::PopulateSinks(
|
||||
const VideoConfig& config = it->second;
|
||||
|
||||
std::vector<std::unique_ptr<rtc::VideoSinkInterface<VideoFrame>>> sinks;
|
||||
test::VideoFrameWriter* writer = nullptr;
|
||||
if (config.output_dump_options.has_value()) {
|
||||
writer = CreateVideoWriter(
|
||||
std::unique_ptr<test::VideoFrameWriter> writer = CreateVideoFrameWriter(
|
||||
config.output_dump_options->GetOutputDumpFileName(
|
||||
receiver_stream.stream_label, receiver_stream.peer_name),
|
||||
config.output_dump_options->GetOutputFrameIdsDumpFileName(
|
||||
receiver_stream.stream_label, receiver_stream.peer_name),
|
||||
config);
|
||||
config.GetResolution());
|
||||
if (config.output_dump_use_fixed_framerate) {
|
||||
writer = std::make_unique<test::FixedFpsVideoFrameWriterAdapter>(
|
||||
config.fps, clock_, std::move(writer));
|
||||
}
|
||||
sinks.push_back(std::make_unique<VideoWriter>(
|
||||
writer, config.output_dump_options->sampling_modulo()));
|
||||
writer.get(), config.output_dump_options->sampling_modulo()));
|
||||
video_writers_.push_back(std::move(writer));
|
||||
}
|
||||
if (config.show_on_screen) {
|
||||
sinks.push_back(absl::WrapUnique(
|
||||
|
||||
@ -134,38 +134,6 @@ class VideoQualityAnalyzerInjectionHelper : public StatsObserverInterface {
|
||||
}
|
||||
};
|
||||
|
||||
class VideoFrameIdsWriter final : public test::VideoFrameWriter {
|
||||
public:
|
||||
explicit VideoFrameIdsWriter(absl::string_view file_name);
|
||||
~VideoFrameIdsWriter() { Close(); }
|
||||
|
||||
bool WriteFrame(const VideoFrame& frame) override;
|
||||
|
||||
void Close() override;
|
||||
|
||||
private:
|
||||
const std::string file_name_;
|
||||
FILE* output_file_;
|
||||
};
|
||||
|
||||
class VideoWriter final : public rtc::VideoSinkInterface<VideoFrame> {
|
||||
public:
|
||||
VideoWriter(test::VideoFrameWriter* video_writer, int sampling_modulo);
|
||||
~VideoWriter() override = default;
|
||||
|
||||
void OnFrame(const VideoFrame& frame) override;
|
||||
|
||||
private:
|
||||
test::VideoFrameWriter* const video_writer_;
|
||||
const int sampling_modulo_;
|
||||
|
||||
int64_t frames_counter_ = 0;
|
||||
};
|
||||
|
||||
test::VideoFrameWriter* CreateVideoWriter(
|
||||
absl::string_view file_name,
|
||||
absl::optional<std::string> frame_ids_dump_file_name,
|
||||
const PeerConnectionE2EQualityTestFixture::VideoConfig& config);
|
||||
// Creates a deep copy of the frame and passes it to the video analyzer, while
|
||||
// passing real frame to the sinks
|
||||
void OnFrame(absl::string_view peer_name, const VideoFrame& frame);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user