[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:
Artem Titov 2022-10-11 10:16:22 +02:00 committed by WebRTC LUCI CQ
parent a97dc0579c
commit 6b75058774
6 changed files with 455 additions and 133 deletions

View File

@ -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",

View 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

View 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_

View 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

View File

@ -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(

View File

@ -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);