Add optional visualization file writers to VideoProcessor tests.

The purpose of this visualization CL is to add the ability to record
video at the source, after encode, and after decode, in the VideoProcessor
tests. These output files can then be replayed and used as a subjective
complement to the objective metric plots given by the existing Python
plotting script.

BUG=webrtc:6634

Review-Url: https://codereview.webrtc.org/2700493006
Cr-Commit-Position: refs/heads/master@{#16738}
This commit is contained in:
brandtr
2017-02-21 03:59:15 -08:00
committed by Commit bot
parent 7d59f6b1c4
commit 872104ac41
20 changed files with 669 additions and 349 deletions

View File

@ -12,6 +12,7 @@
namespace webrtc {
namespace test {
namespace {
// Codec settings.
const int kBitrates[] = {30, 50, 100, 200, 300, 500, 1000};
@ -26,6 +27,12 @@ const bool kHwCodec = false;
// Packet loss probability [0.0, 1.0].
const float kPacketLoss = 0.0f;
const VisualizationParams kVisualizationParams = {
false, // save_source_y4m
false, // save_encoded_ivf
false, // save_decoded_y4m
};
const bool kVerboseLogging = true;
} // namespace
@ -74,7 +81,7 @@ class PlotVideoProcessorIntegrationTest
0, // num_spatial_resizes
1); // num_key_frames
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, &kVisualizationParams);
}
const int bitrate_;
const int framerate_;

View File

@ -29,6 +29,10 @@
namespace webrtc {
namespace test {
namespace {
const int k90khzTimestampFrameDiff = 3000; // Assuming 30 fps.
} // namespace
const char* ExcludeFrameTypesToStr(ExcludeFrameTypes e) {
switch (e) {
case kExcludeOnlyFirstKeyFrame:
@ -60,18 +64,24 @@ TestConfig::~TestConfig() {}
VideoProcessorImpl::VideoProcessorImpl(webrtc::VideoEncoder* encoder,
webrtc::VideoDecoder* decoder,
FrameReader* frame_reader,
FrameWriter* frame_writer,
FrameReader* analysis_frame_reader,
FrameWriter* analysis_frame_writer,
PacketManipulator* packet_manipulator,
const TestConfig& config,
Stats* stats)
Stats* stats,
FrameWriter* source_frame_writer,
IvfFileWriter* encoded_frame_writer,
FrameWriter* decoded_frame_writer)
: encoder_(encoder),
decoder_(decoder),
frame_reader_(frame_reader),
frame_writer_(frame_writer),
analysis_frame_reader_(analysis_frame_reader),
analysis_frame_writer_(analysis_frame_writer),
packet_manipulator_(packet_manipulator),
config_(config),
stats_(stats),
source_frame_writer_(source_frame_writer),
encoded_frame_writer_(encoded_frame_writer),
decoded_frame_writer_(decoded_frame_writer),
first_key_frame_has_been_excluded_(false),
last_frame_missing_(false),
initialized_(false),
@ -94,8 +104,8 @@ VideoProcessorImpl::VideoProcessorImpl(webrtc::VideoEncoder* encoder,
*config.codec_settings, std::move(tl_factory));
RTC_DCHECK(encoder);
RTC_DCHECK(decoder);
RTC_DCHECK(frame_reader);
RTC_DCHECK(frame_writer);
RTC_DCHECK(analysis_frame_reader);
RTC_DCHECK(analysis_frame_writer);
RTC_DCHECK(packet_manipulator);
RTC_DCHECK(stats);
}
@ -105,7 +115,7 @@ bool VideoProcessorImpl::Init() {
bit_rate_factor_ = config_.codec_settings->maxFramerate * 0.001 * 8; // bits
// Initialize data structures used by the encoder/decoder APIs.
size_t frame_length_in_bytes = frame_reader_->FrameLength();
size_t frame_length_in_bytes = analysis_frame_reader_->FrameLength();
last_successful_frame_buffer_.reset(new uint8_t[frame_length_in_bytes]);
// Set fixed properties common for all frames.
@ -141,7 +151,8 @@ bool VideoProcessorImpl::Init() {
if (config_.verbose) {
printf("Video Processor:\n");
printf(" #CPU cores used : %d\n", num_cores);
printf(" Total # of frames: %d\n", frame_reader_->NumberOfFrames());
printf(" Total # of frames: %d\n",
analysis_frame_reader_->NumberOfFrames());
printf(" Codec settings:\n");
printf(" Start bitrate : %d kbps\n",
config_.codec_settings->startBitrate);
@ -207,15 +218,26 @@ bool VideoProcessorImpl::ProcessFrame(int frame_number) {
RTC_DCHECK_GE(frame_number, 0);
RTC_CHECK(initialized_) << "Attempting to use uninitialized VideoProcessor";
// |prev_time_stamp_| is used for getting number of dropped frames.
if (frame_number == 0) {
prev_time_stamp_ = -1;
}
rtc::scoped_refptr<VideoFrameBuffer> buffer(frame_reader_->ReadFrame());
rtc::scoped_refptr<VideoFrameBuffer> buffer(
analysis_frame_reader_->ReadFrame());
if (buffer) {
// Use the frame number as "timestamp" to identify frames.
VideoFrame source_frame(buffer, frame_number, 0, webrtc::kVideoRotation_0);
if (source_frame_writer_) {
// TODO(brandtr): Introduce temp buffer as data member, to avoid
// allocating for every frame.
size_t length = CalcBufferSize(kI420, buffer->width(), buffer->height());
std::unique_ptr<uint8_t[]> extracted_buffer(new uint8_t[length]);
int extracted_length =
ExtractBuffer(buffer, length, extracted_buffer.get());
RTC_DCHECK_GT(extracted_length, 0);
source_frame_writer_->WriteFrame(extracted_buffer.get());
}
// Use the frame number as basis for timestamp to identify frames. Let the
// first timestamp be non-zero, to not make the IvfFileWriter believe that
// we want to use capture timestamps in the IVF files.
VideoFrame source_frame(buffer,
(frame_number + 1) * k90khzTimestampFrameDiff, 0,
webrtc::kVideoRotation_0);
// Ensure we have a new statistics data object we can fill.
FrameStatistic& stat = stats_->NewFrame(frame_number);
@ -258,30 +280,41 @@ void VideoProcessorImpl::FrameEncoded(
// time recordings should wrap the Encode call as tightly as possible.
int64_t encode_stop_ns = rtc::TimeNanos();
// Timestamp is frame number, so this gives us #dropped frames.
if (encoded_frame_writer_) {
RTC_DCHECK(encoded_frame_writer_->WriteFrame(encoded_image, codec));
}
// Timestamp is proportional to frame number, so this gives us number of
// dropped frames.
int num_dropped_from_prev_encode =
encoded_image._timeStamp - prev_time_stamp_ - 1;
(encoded_image._timeStamp - prev_time_stamp_) / k90khzTimestampFrameDiff -
1;
num_dropped_frames_ += num_dropped_from_prev_encode;
prev_time_stamp_ = encoded_image._timeStamp;
if (num_dropped_from_prev_encode > 0) {
// For dropped frames, we write out the last decoded frame to avoid getting
// out of sync for the computation of PSNR and SSIM.
for (int i = 0; i < num_dropped_from_prev_encode; i++) {
frame_writer_->WriteFrame(last_successful_frame_buffer_.get());
RTC_DCHECK(analysis_frame_writer_->WriteFrame(
last_successful_frame_buffer_.get()));
if (decoded_frame_writer_) {
RTC_DCHECK(decoded_frame_writer_->WriteFrame(
last_successful_frame_buffer_.get()));
}
}
}
// Frame is not dropped, so update the encoded frame size
// (encoder callback is only called for non-zero length frames).
encoded_frame_size_ = encoded_image._length;
encoded_frame_type_ = encoded_image._frameType;
int frame_number = encoded_image._timeStamp;
int frame_number = encoded_image._timeStamp / k90khzTimestampFrameDiff - 1;
FrameStatistic& stat = stats_->stats_[frame_number];
stat.encode_time_in_us =
GetElapsedTimeMicroseconds(encode_start_ns_, encode_stop_ns);
stat.encoding_successful = true;
stat.encoded_frame_length_in_bytes = encoded_image._length;
stat.frame_number = encoded_image._timeStamp;
stat.frame_number = frame_number;
stat.frame_type = encoded_image._frameType;
stat.bit_rate_in_kbps = encoded_image._length * bit_rate_factor_;
stat.total_packets =
@ -338,7 +371,12 @@ void VideoProcessorImpl::FrameEncoded(
if (decode_result != WEBRTC_VIDEO_CODEC_OK) {
// Write the last successful frame the output file to avoid getting it out
// of sync with the source file for SSIM and PSNR comparisons.
frame_writer_->WriteFrame(last_successful_frame_buffer_.get());
RTC_DCHECK(analysis_frame_writer_->WriteFrame(
last_successful_frame_buffer_.get()));
if (decoded_frame_writer_) {
RTC_DCHECK(decoded_frame_writer_->WriteFrame(
last_successful_frame_buffer_.get()));
}
}
// Save status for losses so we can inform the decoder for the next frame.
@ -351,7 +389,7 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
int64_t decode_stop_ns = rtc::TimeNanos();
// Report stats.
int frame_number = image.timestamp();
int frame_number = image.timestamp() / k90khzTimestampFrameDiff - 1;
FrameStatistic& stat = stats_->stats_[frame_number];
stat.decode_time_in_us =
GetElapsedTimeMicroseconds(decode_start_ns_, decode_stop_ns);
@ -389,10 +427,10 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
// Update our copy of the last successful frame.
memcpy(last_successful_frame_buffer_.get(), image_buffer.get(),
extracted_length);
bool write_success = frame_writer_->WriteFrame(image_buffer.get());
RTC_DCHECK(write_success);
if (!write_success) {
fprintf(stderr, "Failed to write frame %d to disk!", frame_number);
RTC_DCHECK(analysis_frame_writer_->WriteFrame(image_buffer.get()));
if (decoded_frame_writer_) {
RTC_DCHECK(decoded_frame_writer_->WriteFrame(image_buffer.get()));
}
} else { // No resize.
// Update our copy of the last successful frame.
@ -412,10 +450,9 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
memcpy(last_successful_frame_buffer_.get(), image_buffer.get(),
extracted_length);
bool write_success = frame_writer_->WriteFrame(image_buffer.get());
RTC_DCHECK(write_success);
if (!write_success) {
fprintf(stderr, "Failed to write frame %d to disk!", frame_number);
RTC_DCHECK(analysis_frame_writer_->WriteFrame(image_buffer.get()));
if (decoded_frame_writer_) {
RTC_DCHECK(decoded_frame_writer_->WriteFrame(image_buffer.get()));
}
}
}

View File

@ -20,6 +20,7 @@
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
#include "webrtc/modules/video_coding/codecs/test/packet_manipulator.h"
#include "webrtc/modules/video_coding/codecs/test/stats.h"
#include "webrtc/modules/video_coding/utility/ivf_file_writer.h"
#include "webrtc/test/testsupport/frame_reader.h"
#include "webrtc/test/testsupport/frame_writer.h"
@ -161,11 +162,14 @@ class VideoProcessorImpl : public VideoProcessor {
public:
VideoProcessorImpl(webrtc::VideoEncoder* encoder,
webrtc::VideoDecoder* decoder,
FrameReader* frame_reader,
FrameWriter* frame_writer,
FrameReader* analysis_frame_reader,
FrameWriter* analysis_frame_writer,
PacketManipulator* packet_manipulator,
const TestConfig& config,
Stats* stats);
Stats* stats,
FrameWriter* source_frame_writer,
IvfFileWriter* encoded_frame_writer,
FrameWriter* decoded_frame_writer);
virtual ~VideoProcessorImpl();
bool Init() override;
bool ProcessFrame(int frame_number) override;
@ -248,12 +252,24 @@ class VideoProcessorImpl : public VideoProcessor {
webrtc::VideoEncoder* const encoder_;
webrtc::VideoDecoder* const decoder_;
std::unique_ptr<VideoBitrateAllocator> bitrate_allocator_;
FrameReader* const frame_reader_;
FrameWriter* const frame_writer_;
// These (mandatory) file manipulators are used for, e.g., objective PSNR and
// SSIM calculations at the end of a test run.
FrameReader* const analysis_frame_reader_;
FrameWriter* const analysis_frame_writer_;
PacketManipulator* const packet_manipulator_;
const TestConfig& config_;
Stats* stats_;
// These (optional) file writers are used for persistently storing the output
// of the coding pipeline at different stages: pre encode (source), post
// encode (encoded), and post decode (decoded). The purpose is to give the
// experimenter an option to subjectively evaluate the quality of the
// encoding, given the test settings. Each frame writer is enabled by being
// non-null.
FrameWriter* const source_frame_writer_;
IvfFileWriter* const encoded_frame_writer_;
FrameWriter* const decoded_frame_writer_;
// Adapters for the codec callbacks.
std::unique_ptr<EncodedImageCallback> encode_callback_;
std::unique_ptr<DecodedImageCallback> decode_callback_;

View File

@ -43,10 +43,8 @@ TEST_F(VideoProcessorIntegrationTest, Process0PercentPacketLossH264) {
// Metrics for rate control.
RateControlMetrics rc_metrics[1];
SetRateControlMetrics(rc_metrics, 0, 2, 60, 20, 10, 20, 0, 1);
ProcessFramesAndVerify(quality_metrics,
rate_profile,
process_settings,
rc_metrics);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics, nullptr /* visualization_params */);
}
#endif // defined(WEBRTC_VIDEOPROCESSOR_H264_TESTS)
@ -75,7 +73,7 @@ TEST_F(VideoProcessorIntegrationTest, Process0PercentPacketLossVP9) {
RateControlMetrics rc_metrics[1];
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 20, 0, 1);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
// VP9: Run with 5% packet loss and fixed bitrate. Quality should be a bit
@ -97,7 +95,7 @@ TEST_F(VideoProcessorIntegrationTest, Process5PercentPacketLossVP9) {
RateControlMetrics rc_metrics[1];
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 20, 0, 1);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
// VP9: Run with no packet loss, with varying bitrate (3 rate updates):
@ -125,7 +123,7 @@ TEST_F(VideoProcessorIntegrationTest, ProcessNoLossChangeBitRateVP9) {
SetRateControlMetrics(rc_metrics, 1, 2, 0, 20, 20, 60, 0, 0);
SetRateControlMetrics(rc_metrics, 2, 0, 0, 25, 20, 40, 0, 0);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
// VP9: Run with no packet loss, with an update (decrease) in frame rate.
@ -158,7 +156,7 @@ TEST_F(VideoProcessorIntegrationTest,
SetRateControlMetrics(rc_metrics, 1, 10, 0, 40, 10, 30, 0, 0);
SetRateControlMetrics(rc_metrics, 2, 5, 0, 30, 5, 20, 0, 0);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
// VP9: Run with no packet loss and denoiser on. One key frame (first frame).
@ -179,7 +177,7 @@ TEST_F(VideoProcessorIntegrationTest, ProcessNoLossDenoiserOnVP9) {
RateControlMetrics rc_metrics[1];
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 20, 0, 1);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
// Run with no packet loss, at low bitrate.
@ -204,7 +202,7 @@ TEST_F(VideoProcessorIntegrationTest,
RateControlMetrics rc_metrics[1];
SetRateControlMetrics(rc_metrics, 0, 228, 70, 160, 15, 80, 1, 1);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
// TODO(marpan): Add temporal layer test for VP9, once changes are in
@ -232,7 +230,7 @@ TEST_F(VideoProcessorIntegrationTest, ProcessZeroPacketLoss) {
RateControlMetrics rc_metrics[1];
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0, 1);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
// VP8: Run with 5% packet loss and fixed bitrate. Quality should be a bit
@ -254,7 +252,7 @@ TEST_F(VideoProcessorIntegrationTest, Process5PercentPacketLoss) {
RateControlMetrics rc_metrics[1];
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0, 1);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
// VP8: Run with 10% packet loss and fixed bitrate. Quality should be lower.
@ -276,7 +274,7 @@ TEST_F(VideoProcessorIntegrationTest, Process10PercentPacketLoss) {
RateControlMetrics rc_metrics[1];
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0, 1);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
#endif // !defined(WEBRTC_IOS)
@ -322,7 +320,7 @@ TEST_F(VideoProcessorIntegrationTest, MAYBE_ProcessNoLossChangeBitRateVP8) {
SetRateControlMetrics(rc_metrics, 1, 0, 0, 25, 20, 10, 0, 0);
SetRateControlMetrics(rc_metrics, 2, 0, 0, 25, 15, 10, 0, 0);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
// VP8: Run with no packet loss, with an update (decrease) in frame rate.
@ -363,7 +361,7 @@ TEST_F(VideoProcessorIntegrationTest,
SetRateControlMetrics(rc_metrics, 1, 10, 0, 25, 10, 35, 0, 0);
SetRateControlMetrics(rc_metrics, 2, 0, 0, 20, 10, 15, 0, 0);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
// VP8: Run with no packet loss, with 3 temporal layers, with a rate update in
@ -398,7 +396,7 @@ TEST_F(VideoProcessorIntegrationTest, MAYBE_ProcessNoLossTemporalLayersVP8) {
SetRateControlMetrics(rc_metrics, 0, 0, 20, 30, 10, 10, 0, 1);
SetRateControlMetrics(rc_metrics, 1, 0, 0, 30, 15, 10, 0, 0);
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
rc_metrics);
rc_metrics, nullptr /* visualization_params */);
}
} // namespace test
} // namespace webrtc

View File

@ -15,6 +15,7 @@
#include <memory>
#include <string>
#include <utility>
#if defined(WEBRTC_ANDROID)
#include "webrtc/modules/video_coding/codecs/test/android_test_initializer.h"
@ -26,6 +27,7 @@
#endif
#include "webrtc/base/checks.h"
#include "webrtc/base/file.h"
#include "webrtc/media/engine/webrtcvideodecoderfactory.h"
#include "webrtc/media/engine/webrtcvideoencoderfactory.h"
#include "webrtc/modules/video_coding/codecs/h264/include/h264.h"
@ -33,10 +35,10 @@
#include "webrtc/modules/video_coding/codecs/test/videoprocessor.h"
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8_common_types.h"
#include "webrtc/modules/video_coding/codecs/vp8/temporal_layers.h"
#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h"
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
#include "webrtc/modules/video_coding/include/video_coding.h"
#include "webrtc/modules/video_coding/utility/ivf_file_writer.h"
#include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/test/testsupport/frame_reader.h"
@ -109,6 +111,13 @@ struct RateControlMetrics {
int num_key_frames;
};
// Should video files be saved persistently to disk for post-run visualization?
struct VisualizationParams {
bool save_source_y4m;
bool save_encoded_ivf;
bool save_decoded_y4m;
};
#if !defined(WEBRTC_IOS)
const int kNumFramesShort = 100;
#endif
@ -145,7 +154,8 @@ class VideoProcessorIntegrationTest : public testing::Test {
void SetUpCodecConfig(const std::string& filename,
int width,
int height,
bool verbose_logging) {
bool verbose_logging,
const VisualizationParams* visualization_params) {
if (hw_codec_) {
#if defined(WEBRTC_VIDEOPROCESSOR_INTEGRATIONTEST_HW_CODECS_ENABLED)
#if defined(WEBRTC_ANDROID)
@ -248,19 +258,54 @@ class VideoProcessorIntegrationTest : public testing::Test {
RTC_NOTREACHED();
break;
}
frame_reader_.reset(new test::FrameReaderImpl(
// Create file objects for quality analysis.
analysis_frame_reader_.reset(new test::YuvFrameReaderImpl(
config_.input_filename, config_.codec_settings->width,
config_.codec_settings->height));
frame_writer_.reset(new test::FrameWriterImpl(
config_.output_filename, config_.frame_length_in_bytes));
RTC_CHECK(frame_reader_->Init());
RTC_CHECK(frame_writer_->Init());
analysis_frame_writer_.reset(new test::YuvFrameWriterImpl(
config_.output_filename, config_.codec_settings->width,
config_.codec_settings->height));
RTC_CHECK(analysis_frame_reader_->Init());
RTC_CHECK(analysis_frame_writer_->Init());
if (visualization_params) {
// clang-format off
const std::string output_filename_base =
test::OutputPath() + filename +
"_cd-" + CodecTypeToPayloadName(codec_type_).value_or("") +
"_hw-" + std::to_string(hw_codec_) +
"_fr-" + std::to_string(start_frame_rate_) +
"_br-" + std::to_string(static_cast<int>(start_bitrate_));
// clang-format on
if (visualization_params->save_source_y4m) {
source_frame_writer_.reset(new test::Y4mFrameWriterImpl(
output_filename_base + "_source.y4m", config_.codec_settings->width,
config_.codec_settings->height, start_frame_rate_));
RTC_CHECK(source_frame_writer_->Init());
}
if (visualization_params->save_encoded_ivf) {
rtc::File post_encode_file =
rtc::File::Create(output_filename_base + "_encoded.ivf");
encoded_frame_writer_ =
IvfFileWriter::Wrap(std::move(post_encode_file), 0);
}
if (visualization_params->save_decoded_y4m) {
decoded_frame_writer_.reset(new test::Y4mFrameWriterImpl(
output_filename_base + "_decoded.y4m",
config_.codec_settings->width, config_.codec_settings->height,
start_frame_rate_));
RTC_CHECK(decoded_frame_writer_->Init());
}
}
packet_manipulator_.reset(new test::PacketManipulatorImpl(
&packet_reader_, config_.networking_config, config_.verbose));
processor_.reset(new test::VideoProcessorImpl(
encoder_.get(), decoder_.get(), frame_reader_.get(),
frame_writer_.get(), packet_manipulator_.get(), config_, &stats_));
encoder_.get(), decoder_.get(), analysis_frame_reader_.get(),
analysis_frame_writer_.get(), packet_manipulator_.get(), config_,
&stats_, source_frame_writer_.get(), encoded_frame_writer_.get(),
decoded_frame_writer_.get()));
RTC_CHECK(processor_->Init());
}
@ -446,11 +491,13 @@ class VideoProcessorIntegrationTest : public testing::Test {
void ProcessFramesAndVerify(QualityMetrics quality_metrics,
RateProfile rate_profile,
CodecConfigPars process,
RateControlMetrics* rc_metrics) {
RateControlMetrics* rc_metrics,
const VisualizationParams* visualization_params) {
// Codec/config settings.
codec_type_ = process.codec_type;
hw_codec_ = process.hw_codec;
start_bitrate_ = rate_profile.target_bit_rate[0];
start_frame_rate_ = rate_profile.input_frame_rate[0];
packet_loss_ = process.packet_loss;
key_frame_interval_ = process.key_frame_interval;
num_temporal_layers_ = process.num_temporal_layers;
@ -459,7 +506,8 @@ class VideoProcessorIntegrationTest : public testing::Test {
frame_dropper_on_ = process.frame_dropper_on;
spatial_resize_on_ = process.spatial_resize_on;
SetUpCodecConfig(process.filename, process.width, process.height,
process.verbose_logging);
process.verbose_logging, visualization_params);
// Update the layers and the codec with the initial rates.
bit_rate_ = rate_profile.target_bit_rate[0];
frame_rate_ = rate_profile.input_frame_rate[0];
@ -522,20 +570,31 @@ class VideoProcessorIntegrationTest : public testing::Test {
EXPECT_EQ(num_frames + 1, static_cast<int>(stats_.stats_.size()));
// Release encoder and decoder to make sure they have finished processing:
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release());
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Release());
RTC_DCHECK_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release());
RTC_DCHECK_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Release());
// Close the files before we start using them for SSIM/PSNR calculations.
frame_reader_->Close();
frame_writer_->Close();
// Close the analysis files before we use them for SSIM/PSNR calculations.
analysis_frame_reader_->Close();
analysis_frame_writer_->Close();
// TODO(marpan): should compute these quality metrics per SetRates update.
// Close visualization files.
if (source_frame_writer_) {
source_frame_writer_->Close();
}
if (encoded_frame_writer_) {
encoded_frame_writer_->Close();
}
if (decoded_frame_writer_) {
decoded_frame_writer_->Close();
}
// TODO(marpan): Should compute these quality metrics per SetRates update.
test::QualityMetricsResult psnr_result, ssim_result;
EXPECT_EQ(0, test::I420MetricsFromFiles(config_.input_filename.c_str(),
config_.output_filename.c_str(),
config_.codec_settings->width,
config_.codec_settings->height,
&psnr_result, &ssim_result));
RTC_DCHECK_EQ(0, test::I420MetricsFromFiles(config_.input_filename.c_str(),
config_.output_filename.c_str(),
config_.codec_settings->width,
config_.codec_settings->height,
&psnr_result, &ssim_result));
printf("PSNR avg: %f, min: %f\nSSIM avg: %f, min: %f\n",
psnr_result.average, psnr_result.min, ssim_result.average,
ssim_result.min);
@ -544,22 +603,13 @@ class VideoProcessorIntegrationTest : public testing::Test {
EXPECT_GT(psnr_result.min, quality_metrics.minimum_min_psnr);
EXPECT_GT(ssim_result.average, quality_metrics.minimum_avg_ssim);
EXPECT_GT(ssim_result.min, quality_metrics.minimum_min_ssim);
// Remove analysis file.
if (remove(config_.output_filename.c_str()) < 0) {
fprintf(stderr, "Failed to remove temporary file!\n");
}
}
static void SetRateProfilePars(RateProfile* rate_profile,
int update_index,
int bit_rate,
int frame_rate,
int frame_index_rate_update) {
rate_profile->target_bit_rate[update_index] = bit_rate;
rate_profile->input_frame_rate[update_index] = frame_rate;
rate_profile->frame_index_rate_update[update_index] =
frame_index_rate_update;
}
static void SetCodecParameters(CodecConfigPars* process_settings,
VideoCodecType codec_type,
bool hw_codec,
@ -617,6 +667,17 @@ class VideoProcessorIntegrationTest : public testing::Test {
quality_metrics->minimum_min_ssim = minimum_min_ssim;
}
static void SetRateProfilePars(RateProfile* rate_profile,
int update_index,
int bit_rate,
int frame_rate,
int frame_index_rate_update) {
rate_profile->target_bit_rate[update_index] = bit_rate;
rate_profile->input_frame_rate[update_index] = frame_rate;
rate_profile->frame_index_rate_update[update_index] =
frame_index_rate_update;
}
static void SetRateControlMetrics(RateControlMetrics* rc_metrics,
int update_index,
int max_num_dropped_frames,
@ -638,20 +699,27 @@ class VideoProcessorIntegrationTest : public testing::Test {
rc_metrics[update_index].num_key_frames = num_key_frames;
}
// Codecs.
std::unique_ptr<VideoEncoder> encoder_;
std::unique_ptr<cricket::WebRtcVideoEncoderFactory> external_encoder_factory_;
std::unique_ptr<VideoDecoder> decoder_;
std::unique_ptr<cricket::WebRtcVideoDecoderFactory> external_decoder_factory_;
std::unique_ptr<test::FrameReader> frame_reader_;
std::unique_ptr<test::FrameWriter> frame_writer_;
VideoCodec codec_settings_;
// Helper objects.
std::unique_ptr<test::FrameReader> analysis_frame_reader_;
std::unique_ptr<test::FrameWriter> analysis_frame_writer_;
test::PacketReader packet_reader_;
std::unique_ptr<test::PacketManipulator> packet_manipulator_;
test::Stats stats_;
test::TestConfig config_;
VideoCodec codec_settings_;
// Must be destroyed before |encoder_| and |decoder_|.
std::unique_ptr<test::VideoProcessor> processor_;
TemporalLayersFactory tl_factory_;
// Visualization objects.
std::unique_ptr<test::FrameWriter> source_frame_writer_;
std::unique_ptr<IvfFileWriter> encoded_frame_writer_;
std::unique_ptr<test::FrameWriter> decoded_frame_writer_;
// Quantities defined/updated for every encoder rate update.
// Some quantities defined per temporal layer (at most 3 layers in this test).
@ -676,6 +744,7 @@ class VideoProcessorIntegrationTest : public testing::Test {
float sum_key_frame_size_mismatch_;
int num_key_frames_;
float start_bitrate_;
int start_frame_rate_;
// Codec and network settings.
VideoCodecType codec_type_;

View File

@ -69,7 +69,9 @@ TEST_F(VideoProcessorTest, Init) {
ExpectInit();
VideoProcessorImpl video_processor(
&encoder_mock_, &decoder_mock_, &frame_reader_mock_, &frame_writer_mock_,
&packet_manipulator_mock_, config_, &stats_);
&packet_manipulator_mock_, config_, &stats_,
nullptr /* source_frame_writer */, nullptr /* encoded_frame_writer */,
nullptr /* decoded_frame_writer */);
ASSERT_TRUE(video_processor.Init());
}
@ -82,7 +84,9 @@ TEST_F(VideoProcessorTest, ProcessFrame) {
// be more than initialized...
VideoProcessorImpl video_processor(
&encoder_mock_, &decoder_mock_, &frame_reader_mock_, &frame_writer_mock_,
&packet_manipulator_mock_, config_, &stats_);
&packet_manipulator_mock_, config_, &stats_,
nullptr /* source_frame_writer */, nullptr /* encoded_frame_writer */,
nullptr /* decoded_frame_writer */);
ASSERT_TRUE(video_processor.Init());
video_processor.ProcessFrame(0);
}