Reland of Add optional visualization file writers to VideoProcessor tests. (patchset #1 id:1 of https://codereview.webrtc.org/2708103002/ )

Reason for revert:
Necessary calls were "protected" by RTC_DCHECKs, that were optimized away in some release builds.
Replacing the RTC_DCHECKs with EXPECTs.

Original issue's description:
> Revert of Add optional visualization file writers to VideoProcessor tests. (patchset #4 id:220001 of https://codereview.webrtc.org/2700493006/ )
>
> Reason for revert:
> Breaks downstream project.
>
> Original issue's description:
> > 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}
> > Committed: 872104ac41
>
> TBR=asapersson@webrtc.org,sprang@webrtc.org,kjellander@webrtc.org
> # Skipping CQ checks because original CL landed less than 1 days ago.
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
> BUG=webrtc:6634
>
> Review-Url: https://codereview.webrtc.org/2708103002
> Cr-Commit-Position: refs/heads/master@{#16745}
> Committed: 2a8135a174

TBR=asapersson@webrtc.org,sprang@webrtc.org,kjellander@webrtc.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
BUG=webrtc:6634

Review-Url: https://codereview.webrtc.org/2706123003
Cr-Commit-Position: refs/heads/master@{#16769}
This commit is contained in:
brandtr
2017-02-22 01:26:59 -08:00
committed by Commit bot
parent 798781299f
commit b78bc75e8c
20 changed files with 667 additions and 348 deletions

View File

@ -12,6 +12,7 @@
namespace webrtc {
namespace test {
namespace {
// Codec settings.
const int kBitrates[] = {30, 50, 100, 200, 300, 500, 1000};
@ -27,6 +28,12 @@ const bool kUseSingleCore = true;
// 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
@ -75,7 +82,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.
@ -139,7 +149,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);
@ -202,18 +213,29 @@ int VideoProcessorImpl::NumberSpatialResizes() {
}
bool VideoProcessorImpl::ProcessFrame(int frame_number) {
RTC_DCHECK_GE(frame_number, 0);
RTC_CHECK_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_CHECK_EQ(extracted_length, source_frame_writer_->FrameLength());
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);
@ -256,30 +278,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_CHECK(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_CHECK(analysis_frame_writer_->WriteFrame(
last_successful_frame_buffer_.get()));
if (decoded_frame_writer_) {
RTC_CHECK(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 =
@ -336,7 +369,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_CHECK(analysis_frame_writer_->WriteFrame(
last_successful_frame_buffer_.get()));
if (decoded_frame_writer_) {
RTC_CHECK(decoded_frame_writer_->WriteFrame(
last_successful_frame_buffer_.get()));
}
}
// Save status for losses so we can inform the decoder for the next frame.
@ -349,7 +387,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);
@ -383,14 +421,14 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
CalcBufferSize(kI420, up_image->width(), up_image->height());
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[length]);
int extracted_length = ExtractBuffer(up_image, length, image_buffer.get());
RTC_DCHECK_GT(extracted_length, 0);
RTC_CHECK_GT(extracted_length, 0);
// 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_CHECK(analysis_frame_writer_->WriteFrame(image_buffer.get()));
if (decoded_frame_writer_) {
RTC_CHECK(decoded_frame_writer_->WriteFrame(image_buffer.get()));
}
} else { // No resize.
// Update our copy of the last successful frame.
@ -406,14 +444,13 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
extracted_length =
ExtractBuffer(image.video_frame_buffer(), length, image_buffer.get());
}
RTC_DCHECK_GT(extracted_length, 0);
RTC_CHECK_GT(extracted_length, 0);
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_CHECK(analysis_frame_writer_->WriteFrame(image_buffer.get()));
if (decoded_frame_writer_) {
RTC_CHECK(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

@ -46,10 +46,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)
@ -78,7 +76,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
@ -100,7 +98,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):
@ -128,7 +126,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.
@ -161,7 +159,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).
@ -182,7 +180,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.
@ -207,7 +205,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
@ -235,7 +233,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
@ -257,7 +255,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.
@ -279,7 +277,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)
@ -325,7 +323,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.
@ -366,7 +364,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
@ -401,7 +399,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"
@ -110,6 +112,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
@ -143,7 +152,8 @@ class VideoProcessorIntegrationTest : public testing::Test {
}
virtual ~VideoProcessorIntegrationTest() = default;
void SetUpCodecConfig(const CodecConfigPars& process) {
void SetUpCodecConfig(const CodecConfigPars& process,
const VisualizationParams* visualization_params) {
if (process.hw_codec) {
#if defined(WEBRTC_VIDEOPROCESSOR_INTEGRATIONTEST_HW_CODECS_ENABLED)
#if defined(WEBRTC_ANDROID)
@ -175,7 +185,7 @@ class VideoProcessorIntegrationTest : public testing::Test {
break;
}
#elif defined(WEBRTC_IOS)
RTC_DCHECK_EQ(kVideoCodecH264, process.codec_type)
ASSERT_EQ(kVideoCodecH264, process.codec_type)
<< "iOS HW codecs only support H264.";
encoder_.reset(new H264VideoToolboxEncoder(
cricket::VideoCodec(cricket::kH264CodecName)));
@ -184,8 +194,8 @@ class VideoProcessorIntegrationTest : public testing::Test {
RTC_NOTREACHED() << "Only support HW codecs on Android and iOS.";
#endif
#endif // WEBRTC_VIDEOPROCESSOR_INTEGRATIONTEST_HW_CODECS_ENABLED
RTC_DCHECK(encoder_) << "HW encoder not successfully created.";
RTC_DCHECK(decoder_) << "HW decoder not successfully created.";
RTC_CHECK(encoder_) << "HW encoder not successfully created.";
RTC_CHECK(decoder_) << "HW decoder not successfully created.";
} else {
// SW codecs.
switch (process.codec_type) {
@ -265,19 +275,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() + process.filename +
"_cd-" + CodecTypeToPayloadName(process.codec_type).value_or("") +
"_hw-" + std::to_string(process.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());
}
@ -463,12 +508,14 @@ 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.
start_bitrate_ = rate_profile.target_bit_rate[0];
start_frame_rate_ = rate_profile.input_frame_rate[0];
packet_loss_ = process.packet_loss;
num_temporal_layers_ = process.num_temporal_layers;
SetUpCodecConfig(process);
SetUpCodecConfig(process, 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];
@ -534,11 +581,22 @@ class VideoProcessorIntegrationTest : public testing::Test {
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release());
EXPECT_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(),
@ -553,22 +611,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,
@ -629,6 +678,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,
@ -650,20 +710,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).
@ -688,6 +755,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.
float packet_loss_;

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

View File

@ -490,11 +490,12 @@ int main(int argc, char* argv[]) {
webrtc::VP8Encoder* encoder = webrtc::VP8Encoder::Create();
webrtc::VP8Decoder* decoder = webrtc::VP8Decoder::Create();
webrtc::test::Stats stats;
webrtc::test::FrameReaderImpl frame_reader(config.input_filename,
config.codec_settings->width,
config.codec_settings->height);
webrtc::test::FrameWriterImpl frame_writer(config.output_filename,
config.frame_length_in_bytes);
webrtc::test::YuvFrameReaderImpl frame_reader(config.input_filename,
config.codec_settings->width,
config.codec_settings->height);
webrtc::test::YuvFrameWriterImpl frame_writer(config.output_filename,
config.codec_settings->width,
config.codec_settings->height);
frame_reader.Init();
frame_writer.Init();
webrtc::test::PacketReader packet_reader;
@ -507,9 +508,11 @@ int main(int argc, char* argv[]) {
packet_manipulator.InitializeRandomSeed(time(NULL));
}
webrtc::test::VideoProcessor* processor =
new webrtc::test::VideoProcessorImpl(encoder, decoder, &frame_reader,
&frame_writer, &packet_manipulator,
config, &stats);
new webrtc::test::VideoProcessorImpl(
encoder, decoder, &frame_reader, &frame_writer, &packet_manipulator,
config, &stats, nullptr /* source_frame_writer */,
nullptr /* encoded_frame_writer */,
nullptr /* decoded_frame_writer */);
processor->Init();
int frame_number = 0;