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

@ -351,6 +351,7 @@ if (rtc_include_tests) {
deps = [
":video_codecs_test_framework",
":video_coding",
":video_coding_utility",
":webrtc_h264",
":webrtc_vp8",
":webrtc_vp9",

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(
analysis_frame_reader_->ReadFrame());
if (buffer) {
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());
}
rtc::scoped_refptr<VideoFrameBuffer> buffer(frame_reader_->ReadFrame());
if (buffer) {
// Use the frame number as "timestamp" to identify frames.
VideoFrame source_frame(buffer, frame_number, 0, webrtc::kVideoRotation_0);
// 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,16 +570,27 @@ 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(),
RTC_DCHECK_EQ(0, test::I420MetricsFromFiles(config_.input_filename.c_str(),
config_.output_filename.c_str(),
config_.codec_settings->width,
config_.codec_settings->height,
@ -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);
}

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,
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);
webrtc::test::FrameWriterImpl frame_writer(config.output_filename,
config.frame_length_in_bytes);
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;

View File

@ -129,14 +129,15 @@ rtc_source_set("video_test_support") {
testonly = true
sources = [
"testsupport/frame_reader.cc",
"testsupport/frame_reader.h",
"testsupport/frame_writer.cc",
"testsupport/frame_writer.h",
"testsupport/metrics/video_metrics.cc",
"testsupport/metrics/video_metrics.h",
"testsupport/mock/mock_frame_reader.h",
"testsupport/mock/mock_frame_writer.h",
"testsupport/y4m_frame_writer.cc",
"testsupport/yuv_frame_reader.cc",
"testsupport/yuv_frame_writer.cc",
]
deps = [
@ -259,12 +260,13 @@ rtc_test("test_support_unittests") {
"rtp_file_reader_unittest.cc",
"rtp_file_writer_unittest.cc",
"testsupport/always_passing_unittest.cc",
"testsupport/frame_reader_unittest.cc",
"testsupport/frame_writer_unittest.cc",
"testsupport/isolated_output_unittest.cc",
"testsupport/metrics/video_metrics_unittest.cc",
"testsupport/packet_reader_unittest.cc",
"testsupport/perf_test_unittest.cc",
"testsupport/y4m_frame_writer_unittest.cc",
"testsupport/yuv_frame_reader_unittest.cc",
"testsupport/yuv_frame_writer_unittest.cc",
]
# TODO(jschuh): Bug 1348: fix this warning.

View File

@ -46,14 +46,14 @@ class FrameReader {
virtual int NumberOfFrames() = 0;
};
class FrameReaderImpl : public FrameReader {
class YuvFrameReaderImpl : public FrameReader {
public:
// Creates a file handler. The input file is assumed to exist and be readable.
// Parameters:
// input_filename The file to read from.
// width, height Size of each frame to read.
FrameReaderImpl(std::string input_filename, int width, int height);
~FrameReaderImpl() override;
YuvFrameReaderImpl(std::string input_filename, int width, int height);
~YuvFrameReaderImpl() override;
bool Init() override;
rtc::scoped_refptr<I420Buffer> ReadFrame() override;
void Close() override;
@ -61,10 +61,10 @@ class FrameReaderImpl : public FrameReader {
int NumberOfFrames() override;
private:
std::string input_filename_;
const std::string input_filename_;
size_t frame_length_in_bytes_;
int width_;
int height_;
const int width_;
const int height_;
int number_of_frames_;
FILE* input_file_;
};

View File

@ -1,70 +0,0 @@
/*
* Copyright (c) 2011 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 "webrtc/test/testsupport/frame_reader.h"
#include "webrtc/api/video/i420_buffer.h"
#include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/fileutils.h"
namespace webrtc {
namespace test {
const std::string kInputFileContents = "baz";
const size_t kFrameLength = 3;
class FrameReaderTest: public testing::Test {
protected:
FrameReaderTest() {}
virtual ~FrameReaderTest() {}
void SetUp() {
// Create a dummy input file.
temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
"frame_reader_unittest");
FILE* dummy = fopen(temp_filename_.c_str(), "wb");
fprintf(dummy, "%s", kInputFileContents.c_str());
fclose(dummy);
frame_reader_ = new FrameReaderImpl(temp_filename_, 1, 1);
ASSERT_TRUE(frame_reader_->Init());
}
void TearDown() {
delete frame_reader_;
// Cleanup the dummy input file.
remove(temp_filename_.c_str());
}
FrameReader* frame_reader_;
std::string temp_filename_;
};
TEST_F(FrameReaderTest, InitSuccess) {
FrameReaderImpl frame_reader(temp_filename_, 1, 1);
ASSERT_TRUE(frame_reader.Init());
ASSERT_EQ(kFrameLength, frame_reader.FrameLength());
ASSERT_EQ(1, frame_reader.NumberOfFrames());
}
TEST_F(FrameReaderTest, ReadFrame) {
rtc::scoped_refptr<VideoFrameBuffer> buffer;
buffer = frame_reader_->ReadFrame();
ASSERT_TRUE(buffer);
ASSERT_EQ(kInputFileContents[0], buffer->DataY()[0]);
ASSERT_EQ(kInputFileContents[1], buffer->DataU()[0]);
ASSERT_EQ(kInputFileContents[2], buffer->DataV()[0]);
ASSERT_FALSE(frame_reader_->ReadFrame()); // End of file
}
TEST_F(FrameReaderTest, ReadFrameUninitialized) {
FrameReaderImpl file_reader(temp_filename_, 1, 1);
ASSERT_FALSE(file_reader.ReadFrame());
}
} // namespace test
} // namespace webrtc

View File

@ -1,70 +0,0 @@
/*
* Copyright (c) 2011 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 "webrtc/test/testsupport/frame_writer.h"
#include <assert.h>
namespace webrtc {
namespace test {
FrameWriterImpl::FrameWriterImpl(std::string output_filename,
size_t frame_length_in_bytes)
: output_filename_(output_filename),
frame_length_in_bytes_(frame_length_in_bytes),
output_file_(NULL) {
}
FrameWriterImpl::~FrameWriterImpl() {
Close();
}
bool FrameWriterImpl::Init() {
if (frame_length_in_bytes_ <= 0) {
fprintf(stderr, "Frame length must be >0, was %zu\n",
frame_length_in_bytes_);
return false;
}
output_file_ = fopen(output_filename_.c_str(), "wb");
if (output_file_ == NULL) {
fprintf(stderr, "Couldn't open output file for writing: %s\n",
output_filename_.c_str());
return false;
}
return true;
}
void FrameWriterImpl::Close() {
if (output_file_ != NULL) {
fclose(output_file_);
output_file_ = NULL;
}
}
size_t FrameWriterImpl::FrameLength() { return frame_length_in_bytes_; }
bool FrameWriterImpl::WriteFrame(uint8_t* frame_buffer) {
assert(frame_buffer);
if (output_file_ == NULL) {
fprintf(stderr, "FrameWriter is not initialized (output file is NULL)\n");
return false;
}
size_t bytes_written = fwrite(frame_buffer, 1, frame_length_in_bytes_,
output_file_);
if (bytes_written != frame_length_in_bytes_) {
fprintf(stderr, "Failed to write %zu bytes to file %s\n",
frame_length_in_bytes_, output_filename_.c_str());
return false;
}
return true;
}
} // namespace test
} // namespace webrtc

View File

@ -42,28 +42,46 @@ class FrameWriter {
virtual size_t FrameLength() = 0;
};
class FrameWriterImpl : public FrameWriter {
// Writes raw I420 frames in sequence.
class YuvFrameWriterImpl : public FrameWriter {
public:
// Creates a file handler. The input file is assumed to exist and be readable
// and the output file must be writable.
// Parameters:
// output_filename The file to write. Will be overwritten if already
// existing.
// frame_length_in_bytes The size of each frame.
// For YUV: 3*width*height/2
FrameWriterImpl(std::string output_filename, size_t frame_length_in_bytes);
~FrameWriterImpl() override;
// width, height Size of each frame to read.
YuvFrameWriterImpl(std::string output_filename, int width, int height);
~YuvFrameWriterImpl() override;
bool Init() override;
bool WriteFrame(uint8_t* frame_buffer) override;
void Close() override;
size_t FrameLength() override;
private:
std::string output_filename_;
protected:
const std::string output_filename_;
size_t frame_length_in_bytes_;
const int width_;
const int height_;
FILE* output_file_;
};
// Writes raw I420 frames in sequence, but with Y4M file and frame headers for
// more convenient playback in external media players.
class Y4mFrameWriterImpl : public YuvFrameWriterImpl {
public:
Y4mFrameWriterImpl(std::string output_filename,
int width,
int height,
int frame_rate);
~Y4mFrameWriterImpl() override;
bool Init() override;
bool WriteFrame(uint8_t* frame_buffer) override;
private:
const int frame_rate_;
};
} // namespace test
} // namespace webrtc

View File

@ -1,63 +0,0 @@
/*
* Copyright (c) 2011 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 "webrtc/test/testsupport/frame_writer.h"
#include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/fileutils.h"
namespace webrtc {
namespace test {
const size_t kFrameLength = 1000;
class FrameWriterTest: public testing::Test {
protected:
FrameWriterTest() {}
virtual ~FrameWriterTest() {}
void SetUp() {
temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
"frame_writer_unittest");
frame_writer_ = new FrameWriterImpl(temp_filename_, kFrameLength);
ASSERT_TRUE(frame_writer_->Init());
}
void TearDown() {
delete frame_writer_;
// Cleanup the temporary file.
remove(temp_filename_.c_str());
}
FrameWriter* frame_writer_;
std::string temp_filename_;
};
TEST_F(FrameWriterTest, InitSuccess) {
FrameWriterImpl frame_writer(temp_filename_, kFrameLength);
ASSERT_TRUE(frame_writer.Init());
ASSERT_EQ(kFrameLength, frame_writer.FrameLength());
}
TEST_F(FrameWriterTest, WriteFrame) {
uint8_t buffer[kFrameLength];
memset(buffer, 9, kFrameLength); // Write lots of 9s to the buffer
bool result = frame_writer_->WriteFrame(buffer);
ASSERT_TRUE(result); // success
// Close the file and verify the size.
frame_writer_->Close();
ASSERT_EQ(kFrameLength, GetFileSize(temp_filename_));
}
TEST_F(FrameWriterTest, WriteFrameUninitialized) {
uint8_t buffer[3];
FrameWriterImpl frame_writer(temp_filename_, kFrameLength);
ASSERT_FALSE(frame_writer.WriteFrame(buffer));
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2017 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 "webrtc/base/checks.h"
#include "webrtc/test/testsupport/frame_writer.h"
namespace webrtc {
namespace test {
Y4mFrameWriterImpl::Y4mFrameWriterImpl(std::string output_filename,
int width,
int height,
int frame_rate)
: YuvFrameWriterImpl(output_filename, width, height),
frame_rate_(frame_rate) {}
Y4mFrameWriterImpl::~Y4mFrameWriterImpl() = default;
bool Y4mFrameWriterImpl::Init() {
if (!YuvFrameWriterImpl::Init()) {
return false;
}
int bytes_written = fprintf(output_file_, "YUV4MPEG2 W%d H%d F%d:1 C420\n",
width_, height_, frame_rate_);
if (bytes_written < 0) {
fprintf(stderr, "Failed to write Y4M file header to file %s\n",
output_filename_.c_str());
return false;
}
return true;
}
bool Y4mFrameWriterImpl::WriteFrame(uint8_t* frame_buffer) {
if (output_file_ == nullptr) {
fprintf(stderr,
"Y4mFrameWriterImpl is not initialized (output file is NULL)\n");
return false;
}
int bytes_written = fprintf(output_file_, "FRAME\n");
if (bytes_written < 0) {
fprintf(stderr, "Failed to write Y4M frame header to file %s\n",
output_filename_.c_str());
return false;
}
return YuvFrameWriterImpl::WriteFrame(frame_buffer);
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2017 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 <memory>
#include <string>
#include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/test/testsupport/frame_writer.h"
namespace webrtc {
namespace test {
namespace {
const size_t kFrameWidth = 50;
const size_t kFrameHeight = 20;
const size_t kFrameLength = 3 * kFrameWidth * kFrameHeight / 2; // I420.
const size_t kFrameRate = 30;
const std::string kFileHeader = "YUV4MPEG2 W50 H20 F30:1 C420\n";
const std::string kFrameHeader = "FRAME\n";
} // namespace
class Y4mFrameWriterTest : public testing::Test {
protected:
Y4mFrameWriterTest() = default;
~Y4mFrameWriterTest() override = default;
void SetUp() override {
temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
"y4m_frame_writer_unittest");
frame_writer_.reset(new Y4mFrameWriterImpl(temp_filename_, kFrameWidth,
kFrameHeight, kFrameRate));
ASSERT_TRUE(frame_writer_->Init());
}
void TearDown() override { remove(temp_filename_.c_str()); }
std::unique_ptr<FrameWriter> frame_writer_;
std::string temp_filename_;
};
TEST_F(Y4mFrameWriterTest, InitSuccess) {}
TEST_F(Y4mFrameWriterTest, FrameLength) {
EXPECT_EQ(kFrameLength, frame_writer_->FrameLength());
}
TEST_F(Y4mFrameWriterTest, WriteFrame) {
uint8_t buffer[kFrameLength];
memset(buffer, 9, kFrameLength); // Write lots of 9s to the buffer.
bool result = frame_writer_->WriteFrame(buffer);
ASSERT_TRUE(result);
result = frame_writer_->WriteFrame(buffer);
ASSERT_TRUE(result);
frame_writer_->Close();
EXPECT_EQ(kFileHeader.size() + 2 * kFrameHeader.size() + 2 * kFrameLength,
GetFileSize(temp_filename_));
}
TEST_F(Y4mFrameWriterTest, WriteFrameUninitialized) {
uint8_t buffer[kFrameLength];
Y4mFrameWriterImpl frame_writer(temp_filename_, kFrameWidth, kFrameHeight,
kFrameRate);
EXPECT_FALSE(frame_writer.WriteFrame(buffer));
}
} // namespace test
} // namespace webrtc

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
* Copyright (c) 2017 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
@ -10,8 +10,6 @@
#include "webrtc/test/testsupport/frame_reader.h"
#include <assert.h>
#include "webrtc/api/video/i420_buffer.h"
#include "webrtc/test/frame_utils.h"
#include "webrtc/test/testsupport/fileutils.h"
@ -19,28 +17,31 @@
namespace webrtc {
namespace test {
FrameReaderImpl::FrameReaderImpl(std::string input_filename,
int width, int height)
YuvFrameReaderImpl::YuvFrameReaderImpl(std::string input_filename,
int width,
int height)
: input_filename_(input_filename),
width_(width), height_(height),
input_file_(NULL) {
}
frame_length_in_bytes_(0),
width_(width),
height_(height),
number_of_frames_(-1),
input_file_(nullptr) {}
FrameReaderImpl::~FrameReaderImpl() {
YuvFrameReaderImpl::~YuvFrameReaderImpl() {
Close();
}
bool FrameReaderImpl::Init() {
bool YuvFrameReaderImpl::Init() {
if (width_ <= 0 || height_ <= 0) {
fprintf(stderr, "Frame width and height must be >0, was %d x %d\n",
width_, height_);
fprintf(stderr, "Frame width and height must be >0, was %d x %d\n", width_,
height_);
return false;
}
frame_length_in_bytes_ =
width_ * height_ + 2 * ((width_ + 1) / 2) * ((height_ + 1) / 2);
input_file_ = fopen(input_filename_.c_str(), "rb");
if (input_file_ == NULL) {
if (input_file_ == nullptr) {
fprintf(stderr, "Couldn't open input file for reading: %s\n",
input_filename_.c_str());
return false;
@ -51,21 +52,15 @@ bool FrameReaderImpl::Init() {
fprintf(stderr, "Found empty file: %s\n", input_filename_.c_str());
return false;
}
number_of_frames_ = static_cast<int>(source_file_size /
frame_length_in_bytes_);
number_of_frames_ =
static_cast<int>(source_file_size / frame_length_in_bytes_);
return true;
}
void FrameReaderImpl::Close() {
if (input_file_ != NULL) {
fclose(input_file_);
input_file_ = NULL;
}
}
rtc::scoped_refptr<I420Buffer> FrameReaderImpl::ReadFrame() {
if (input_file_ == NULL) {
fprintf(stderr, "FrameReader is not initialized (input file is NULL)\n");
rtc::scoped_refptr<I420Buffer> YuvFrameReaderImpl::ReadFrame() {
if (input_file_ == nullptr) {
fprintf(stderr,
"YuvFrameReaderImpl is not initialized (input file is NULL)\n");
return nullptr;
}
rtc::scoped_refptr<I420Buffer> buffer(
@ -77,8 +72,20 @@ rtc::scoped_refptr<I420Buffer> FrameReaderImpl::ReadFrame() {
return buffer;
}
size_t FrameReaderImpl::FrameLength() { return frame_length_in_bytes_; }
int FrameReaderImpl::NumberOfFrames() { return number_of_frames_; }
void YuvFrameReaderImpl::Close() {
if (input_file_ != nullptr) {
fclose(input_file_);
input_file_ = nullptr;
}
}
size_t YuvFrameReaderImpl::FrameLength() {
return frame_length_in_bytes_;
}
int YuvFrameReaderImpl::NumberOfFrames() {
return number_of_frames_;
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,83 @@
/*
* Copyright (c) 2017 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 <memory>
#include <string>
#include "webrtc/api/video/i420_buffer.h"
#include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/test/testsupport/frame_reader.h"
namespace webrtc {
namespace test {
namespace {
const std::string kInputFileContents = "bazouk";
const size_t kFrameWidth = 2;
const size_t kFrameHeight = 2;
const size_t kFrameLength = 3 * kFrameWidth * kFrameHeight / 2; // I420.
} // namespace
class YuvFrameReaderTest : public testing::Test {
protected:
YuvFrameReaderTest() = default;
~YuvFrameReaderTest() override = default;
void SetUp() override {
temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
"yuv_frame_reader_unittest");
FILE* dummy = fopen(temp_filename_.c_str(), "wb");
fprintf(dummy, "%s", kInputFileContents.c_str());
fclose(dummy);
frame_reader_.reset(
new YuvFrameReaderImpl(temp_filename_, kFrameWidth, kFrameHeight));
ASSERT_TRUE(frame_reader_->Init());
}
void TearDown() override { remove(temp_filename_.c_str()); }
std::unique_ptr<FrameReader> frame_reader_;
std::string temp_filename_;
};
TEST_F(YuvFrameReaderTest, InitSuccess) {}
TEST_F(YuvFrameReaderTest, FrameLength) {
EXPECT_EQ(kFrameLength, frame_reader_->FrameLength());
}
TEST_F(YuvFrameReaderTest, NumberOfFrames) {
EXPECT_EQ(1, frame_reader_->NumberOfFrames());
}
TEST_F(YuvFrameReaderTest, ReadFrame) {
rtc::scoped_refptr<VideoFrameBuffer> buffer;
buffer = frame_reader_->ReadFrame();
ASSERT_TRUE(buffer);
// Expect I420 packed as YUV.
EXPECT_EQ(kInputFileContents[0], buffer->DataY()[0]);
EXPECT_EQ(kInputFileContents[1], buffer->DataY()[1]);
EXPECT_EQ(kInputFileContents[2], buffer->DataY()[2]);
EXPECT_EQ(kInputFileContents[3], buffer->DataY()[3]);
EXPECT_EQ(kInputFileContents[4], buffer->DataU()[0]);
EXPECT_EQ(kInputFileContents[5], buffer->DataV()[0]);
EXPECT_FALSE(frame_reader_->ReadFrame()); // End of file.
}
TEST_F(YuvFrameReaderTest, ReadFrameUninitialized) {
YuvFrameReaderImpl file_reader(temp_filename_, kFrameWidth, kFrameHeight);
EXPECT_FALSE(file_reader.ReadFrame());
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2017 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 "webrtc/base/checks.h"
#include "webrtc/test/testsupport/frame_writer.h"
namespace webrtc {
namespace test {
YuvFrameWriterImpl::YuvFrameWriterImpl(std::string output_filename,
int width,
int height)
: output_filename_(output_filename),
frame_length_in_bytes_(0),
width_(width),
height_(height),
output_file_(nullptr) {}
YuvFrameWriterImpl::~YuvFrameWriterImpl() {
Close();
}
bool YuvFrameWriterImpl::Init() {
if (width_ <= 0 || height_ <= 0) {
fprintf(stderr, "Frame width and height must be >0, was %d x %d\n", width_,
height_);
return false;
}
frame_length_in_bytes_ =
width_ * height_ + 2 * ((width_ + 1) / 2) * ((height_ + 1) / 2);
output_file_ = fopen(output_filename_.c_str(), "wb");
if (output_file_ == nullptr) {
fprintf(stderr, "Couldn't open output file for writing: %s\n",
output_filename_.c_str());
return false;
}
return true;
}
bool YuvFrameWriterImpl::WriteFrame(uint8_t* frame_buffer) {
RTC_DCHECK(frame_buffer);
if (output_file_ == nullptr) {
fprintf(stderr,
"YuvFrameWriterImpl is not initialized (output file is NULL)\n");
return false;
}
size_t bytes_written =
fwrite(frame_buffer, 1, frame_length_in_bytes_, output_file_);
if (bytes_written != frame_length_in_bytes_) {
fprintf(stderr, "Failed to write %zu bytes to file %s\n",
frame_length_in_bytes_, output_filename_.c_str());
return false;
}
return true;
}
void YuvFrameWriterImpl::Close() {
if (output_file_ != nullptr) {
fclose(output_file_);
output_file_ = nullptr;
}
}
size_t YuvFrameWriterImpl::FrameLength() {
return frame_length_in_bytes_;
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2017 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 <memory>
#include "webrtc/test/gtest.h"
#include "webrtc/test/testsupport/fileutils.h"
#include "webrtc/test/testsupport/frame_writer.h"
namespace webrtc {
namespace test {
namespace {
const size_t kFrameWidth = 50;
const size_t kFrameHeight = 20;
const size_t kFrameLength = 3 * kFrameWidth * kFrameHeight / 2; // I420.
} // namespace
class YuvFrameWriterTest : public testing::Test {
protected:
YuvFrameWriterTest() = default;
~YuvFrameWriterTest() override = default;
void SetUp() override {
temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
"yuv_frame_writer_unittest");
frame_writer_.reset(
new YuvFrameWriterImpl(temp_filename_, kFrameWidth, kFrameHeight));
ASSERT_TRUE(frame_writer_->Init());
}
void TearDown() override { remove(temp_filename_.c_str()); }
std::unique_ptr<FrameWriter> frame_writer_;
std::string temp_filename_;
};
TEST_F(YuvFrameWriterTest, InitSuccess) {}
TEST_F(YuvFrameWriterTest, FrameLength) {
EXPECT_EQ(kFrameLength, frame_writer_->FrameLength());
}
TEST_F(YuvFrameWriterTest, WriteFrame) {
uint8_t buffer[kFrameLength];
memset(buffer, 9, kFrameLength); // Write lots of 9s to the buffer.
bool result = frame_writer_->WriteFrame(buffer);
ASSERT_TRUE(result);
frame_writer_->Close();
EXPECT_EQ(kFrameLength, GetFileSize(temp_filename_));
}
TEST_F(YuvFrameWriterTest, WriteFrameUninitialized) {
uint8_t buffer[kFrameLength];
YuvFrameWriterImpl frame_writer(temp_filename_, kFrameWidth, kFrameHeight);
EXPECT_FALSE(frame_writer.WriteFrame(buffer));
}
} // namespace test
} // namespace webrtc