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:2a8135a174TBR=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:
@ -351,6 +351,7 @@ if (rtc_include_tests) {
|
|||||||
deps = [
|
deps = [
|
||||||
":video_codecs_test_framework",
|
":video_codecs_test_framework",
|
||||||
":video_coding",
|
":video_coding",
|
||||||
|
":video_coding_utility",
|
||||||
":webrtc_h264",
|
":webrtc_h264",
|
||||||
":webrtc_vp8",
|
":webrtc_vp8",
|
||||||
":webrtc_vp9",
|
":webrtc_vp9",
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Codec settings.
|
// Codec settings.
|
||||||
const int kBitrates[] = {30, 50, 100, 200, 300, 500, 1000};
|
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].
|
// Packet loss probability [0.0, 1.0].
|
||||||
const float kPacketLoss = 0.0f;
|
const float kPacketLoss = 0.0f;
|
||||||
|
|
||||||
|
const VisualizationParams kVisualizationParams = {
|
||||||
|
false, // save_source_y4m
|
||||||
|
false, // save_encoded_ivf
|
||||||
|
false, // save_decoded_y4m
|
||||||
|
};
|
||||||
|
|
||||||
const bool kVerboseLogging = true;
|
const bool kVerboseLogging = true;
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -75,7 +82,7 @@ class PlotVideoProcessorIntegrationTest
|
|||||||
0, // num_spatial_resizes
|
0, // num_spatial_resizes
|
||||||
1); // num_key_frames
|
1); // num_key_frames
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
||||||
rc_metrics);
|
rc_metrics, &kVisualizationParams);
|
||||||
}
|
}
|
||||||
const int bitrate_;
|
const int bitrate_;
|
||||||
const int framerate_;
|
const int framerate_;
|
||||||
|
|||||||
@ -29,6 +29,10 @@
|
|||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const int k90khzTimestampFrameDiff = 3000; // Assuming 30 fps.
|
||||||
|
} // namespace
|
||||||
|
|
||||||
const char* ExcludeFrameTypesToStr(ExcludeFrameTypes e) {
|
const char* ExcludeFrameTypesToStr(ExcludeFrameTypes e) {
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case kExcludeOnlyFirstKeyFrame:
|
case kExcludeOnlyFirstKeyFrame:
|
||||||
@ -60,18 +64,24 @@ TestConfig::~TestConfig() {}
|
|||||||
|
|
||||||
VideoProcessorImpl::VideoProcessorImpl(webrtc::VideoEncoder* encoder,
|
VideoProcessorImpl::VideoProcessorImpl(webrtc::VideoEncoder* encoder,
|
||||||
webrtc::VideoDecoder* decoder,
|
webrtc::VideoDecoder* decoder,
|
||||||
FrameReader* frame_reader,
|
FrameReader* analysis_frame_reader,
|
||||||
FrameWriter* frame_writer,
|
FrameWriter* analysis_frame_writer,
|
||||||
PacketManipulator* packet_manipulator,
|
PacketManipulator* packet_manipulator,
|
||||||
const TestConfig& config,
|
const TestConfig& config,
|
||||||
Stats* stats)
|
Stats* stats,
|
||||||
|
FrameWriter* source_frame_writer,
|
||||||
|
IvfFileWriter* encoded_frame_writer,
|
||||||
|
FrameWriter* decoded_frame_writer)
|
||||||
: encoder_(encoder),
|
: encoder_(encoder),
|
||||||
decoder_(decoder),
|
decoder_(decoder),
|
||||||
frame_reader_(frame_reader),
|
analysis_frame_reader_(analysis_frame_reader),
|
||||||
frame_writer_(frame_writer),
|
analysis_frame_writer_(analysis_frame_writer),
|
||||||
packet_manipulator_(packet_manipulator),
|
packet_manipulator_(packet_manipulator),
|
||||||
config_(config),
|
config_(config),
|
||||||
stats_(stats),
|
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),
|
first_key_frame_has_been_excluded_(false),
|
||||||
last_frame_missing_(false),
|
last_frame_missing_(false),
|
||||||
initialized_(false),
|
initialized_(false),
|
||||||
@ -94,8 +104,8 @@ VideoProcessorImpl::VideoProcessorImpl(webrtc::VideoEncoder* encoder,
|
|||||||
*config.codec_settings, std::move(tl_factory));
|
*config.codec_settings, std::move(tl_factory));
|
||||||
RTC_DCHECK(encoder);
|
RTC_DCHECK(encoder);
|
||||||
RTC_DCHECK(decoder);
|
RTC_DCHECK(decoder);
|
||||||
RTC_DCHECK(frame_reader);
|
RTC_DCHECK(analysis_frame_reader);
|
||||||
RTC_DCHECK(frame_writer);
|
RTC_DCHECK(analysis_frame_writer);
|
||||||
RTC_DCHECK(packet_manipulator);
|
RTC_DCHECK(packet_manipulator);
|
||||||
RTC_DCHECK(stats);
|
RTC_DCHECK(stats);
|
||||||
}
|
}
|
||||||
@ -105,7 +115,7 @@ bool VideoProcessorImpl::Init() {
|
|||||||
bit_rate_factor_ = config_.codec_settings->maxFramerate * 0.001 * 8; // bits
|
bit_rate_factor_ = config_.codec_settings->maxFramerate * 0.001 * 8; // bits
|
||||||
|
|
||||||
// Initialize data structures used by the encoder/decoder APIs.
|
// 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]);
|
last_successful_frame_buffer_.reset(new uint8_t[frame_length_in_bytes]);
|
||||||
|
|
||||||
// Set fixed properties common for all frames.
|
// Set fixed properties common for all frames.
|
||||||
@ -139,7 +149,8 @@ bool VideoProcessorImpl::Init() {
|
|||||||
if (config_.verbose) {
|
if (config_.verbose) {
|
||||||
printf("Video Processor:\n");
|
printf("Video Processor:\n");
|
||||||
printf(" #CPU cores used : %d\n", num_cores);
|
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(" Codec settings:\n");
|
||||||
printf(" Start bitrate : %d kbps\n",
|
printf(" Start bitrate : %d kbps\n",
|
||||||
config_.codec_settings->startBitrate);
|
config_.codec_settings->startBitrate);
|
||||||
@ -202,18 +213,29 @@ int VideoProcessorImpl::NumberSpatialResizes() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool VideoProcessorImpl::ProcessFrame(int frame_number) {
|
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";
|
RTC_CHECK(initialized_) << "Attempting to use uninitialized VideoProcessor";
|
||||||
|
|
||||||
// |prev_time_stamp_| is used for getting number of dropped frames.
|
rtc::scoped_refptr<VideoFrameBuffer> buffer(
|
||||||
if (frame_number == 0) {
|
analysis_frame_reader_->ReadFrame());
|
||||||
prev_time_stamp_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rtc::scoped_refptr<VideoFrameBuffer> buffer(frame_reader_->ReadFrame());
|
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
// Use the frame number as "timestamp" to identify frames.
|
if (source_frame_writer_) {
|
||||||
VideoFrame source_frame(buffer, frame_number, 0, webrtc::kVideoRotation_0);
|
// 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.
|
// Ensure we have a new statistics data object we can fill.
|
||||||
FrameStatistic& stat = stats_->NewFrame(frame_number);
|
FrameStatistic& stat = stats_->NewFrame(frame_number);
|
||||||
@ -256,30 +278,41 @@ void VideoProcessorImpl::FrameEncoded(
|
|||||||
// time recordings should wrap the Encode call as tightly as possible.
|
// time recordings should wrap the Encode call as tightly as possible.
|
||||||
int64_t encode_stop_ns = rtc::TimeNanos();
|
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 =
|
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;
|
num_dropped_frames_ += num_dropped_from_prev_encode;
|
||||||
prev_time_stamp_ = encoded_image._timeStamp;
|
prev_time_stamp_ = encoded_image._timeStamp;
|
||||||
if (num_dropped_from_prev_encode > 0) {
|
if (num_dropped_from_prev_encode > 0) {
|
||||||
// For dropped frames, we write out the last decoded frame to avoid getting
|
// For dropped frames, we write out the last decoded frame to avoid getting
|
||||||
// out of sync for the computation of PSNR and SSIM.
|
// out of sync for the computation of PSNR and SSIM.
|
||||||
for (int i = 0; i < num_dropped_from_prev_encode; i++) {
|
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
|
// Frame is not dropped, so update the encoded frame size
|
||||||
// (encoder callback is only called for non-zero length frames).
|
// (encoder callback is only called for non-zero length frames).
|
||||||
encoded_frame_size_ = encoded_image._length;
|
encoded_frame_size_ = encoded_image._length;
|
||||||
encoded_frame_type_ = encoded_image._frameType;
|
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];
|
FrameStatistic& stat = stats_->stats_[frame_number];
|
||||||
stat.encode_time_in_us =
|
stat.encode_time_in_us =
|
||||||
GetElapsedTimeMicroseconds(encode_start_ns_, encode_stop_ns);
|
GetElapsedTimeMicroseconds(encode_start_ns_, encode_stop_ns);
|
||||||
stat.encoding_successful = true;
|
stat.encoding_successful = true;
|
||||||
stat.encoded_frame_length_in_bytes = encoded_image._length;
|
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.frame_type = encoded_image._frameType;
|
||||||
stat.bit_rate_in_kbps = encoded_image._length * bit_rate_factor_;
|
stat.bit_rate_in_kbps = encoded_image._length * bit_rate_factor_;
|
||||||
stat.total_packets =
|
stat.total_packets =
|
||||||
@ -336,7 +369,12 @@ void VideoProcessorImpl::FrameEncoded(
|
|||||||
if (decode_result != WEBRTC_VIDEO_CODEC_OK) {
|
if (decode_result != WEBRTC_VIDEO_CODEC_OK) {
|
||||||
// Write the last successful frame the output file to avoid getting it out
|
// Write the last successful frame the output file to avoid getting it out
|
||||||
// of sync with the source file for SSIM and PSNR comparisons.
|
// 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.
|
// 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();
|
int64_t decode_stop_ns = rtc::TimeNanos();
|
||||||
|
|
||||||
// Report stats.
|
// Report stats.
|
||||||
int frame_number = image.timestamp();
|
int frame_number = image.timestamp() / k90khzTimestampFrameDiff - 1;
|
||||||
FrameStatistic& stat = stats_->stats_[frame_number];
|
FrameStatistic& stat = stats_->stats_[frame_number];
|
||||||
stat.decode_time_in_us =
|
stat.decode_time_in_us =
|
||||||
GetElapsedTimeMicroseconds(decode_start_ns_, decode_stop_ns);
|
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());
|
CalcBufferSize(kI420, up_image->width(), up_image->height());
|
||||||
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[length]);
|
std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[length]);
|
||||||
int extracted_length = ExtractBuffer(up_image, length, image_buffer.get());
|
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.
|
// Update our copy of the last successful frame.
|
||||||
memcpy(last_successful_frame_buffer_.get(), image_buffer.get(),
|
memcpy(last_successful_frame_buffer_.get(), image_buffer.get(),
|
||||||
extracted_length);
|
extracted_length);
|
||||||
bool write_success = frame_writer_->WriteFrame(image_buffer.get());
|
|
||||||
RTC_DCHECK(write_success);
|
RTC_CHECK(analysis_frame_writer_->WriteFrame(image_buffer.get()));
|
||||||
if (!write_success) {
|
if (decoded_frame_writer_) {
|
||||||
fprintf(stderr, "Failed to write frame %d to disk!", frame_number);
|
RTC_CHECK(decoded_frame_writer_->WriteFrame(image_buffer.get()));
|
||||||
}
|
}
|
||||||
} else { // No resize.
|
} else { // No resize.
|
||||||
// Update our copy of the last successful frame.
|
// Update our copy of the last successful frame.
|
||||||
@ -406,14 +444,13 @@ void VideoProcessorImpl::FrameDecoded(const VideoFrame& image) {
|
|||||||
extracted_length =
|
extracted_length =
|
||||||
ExtractBuffer(image.video_frame_buffer(), length, image_buffer.get());
|
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(),
|
memcpy(last_successful_frame_buffer_.get(), image_buffer.get(),
|
||||||
extracted_length);
|
extracted_length);
|
||||||
|
|
||||||
bool write_success = frame_writer_->WriteFrame(image_buffer.get());
|
RTC_CHECK(analysis_frame_writer_->WriteFrame(image_buffer.get()));
|
||||||
RTC_DCHECK(write_success);
|
if (decoded_frame_writer_) {
|
||||||
if (!write_success) {
|
RTC_CHECK(decoded_frame_writer_->WriteFrame(image_buffer.get()));
|
||||||
fprintf(stderr, "Failed to write frame %d to disk!", frame_number);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
#include "webrtc/modules/video_coding/include/video_codec_interface.h"
|
#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/packet_manipulator.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/test/stats.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_reader.h"
|
||||||
#include "webrtc/test/testsupport/frame_writer.h"
|
#include "webrtc/test/testsupport/frame_writer.h"
|
||||||
|
|
||||||
@ -161,11 +162,14 @@ class VideoProcessorImpl : public VideoProcessor {
|
|||||||
public:
|
public:
|
||||||
VideoProcessorImpl(webrtc::VideoEncoder* encoder,
|
VideoProcessorImpl(webrtc::VideoEncoder* encoder,
|
||||||
webrtc::VideoDecoder* decoder,
|
webrtc::VideoDecoder* decoder,
|
||||||
FrameReader* frame_reader,
|
FrameReader* analysis_frame_reader,
|
||||||
FrameWriter* frame_writer,
|
FrameWriter* analysis_frame_writer,
|
||||||
PacketManipulator* packet_manipulator,
|
PacketManipulator* packet_manipulator,
|
||||||
const TestConfig& config,
|
const TestConfig& config,
|
||||||
Stats* stats);
|
Stats* stats,
|
||||||
|
FrameWriter* source_frame_writer,
|
||||||
|
IvfFileWriter* encoded_frame_writer,
|
||||||
|
FrameWriter* decoded_frame_writer);
|
||||||
virtual ~VideoProcessorImpl();
|
virtual ~VideoProcessorImpl();
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
bool ProcessFrame(int frame_number) override;
|
bool ProcessFrame(int frame_number) override;
|
||||||
@ -248,12 +252,24 @@ class VideoProcessorImpl : public VideoProcessor {
|
|||||||
webrtc::VideoEncoder* const encoder_;
|
webrtc::VideoEncoder* const encoder_;
|
||||||
webrtc::VideoDecoder* const decoder_;
|
webrtc::VideoDecoder* const decoder_;
|
||||||
std::unique_ptr<VideoBitrateAllocator> bitrate_allocator_;
|
std::unique_ptr<VideoBitrateAllocator> bitrate_allocator_;
|
||||||
FrameReader* const frame_reader_;
|
// These (mandatory) file manipulators are used for, e.g., objective PSNR and
|
||||||
FrameWriter* const frame_writer_;
|
// SSIM calculations at the end of a test run.
|
||||||
|
FrameReader* const analysis_frame_reader_;
|
||||||
|
FrameWriter* const analysis_frame_writer_;
|
||||||
PacketManipulator* const packet_manipulator_;
|
PacketManipulator* const packet_manipulator_;
|
||||||
const TestConfig& config_;
|
const TestConfig& config_;
|
||||||
Stats* stats_;
|
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<EncodedImageCallback> encode_callback_;
|
||||||
std::unique_ptr<DecodedImageCallback> decode_callback_;
|
std::unique_ptr<DecodedImageCallback> decode_callback_;
|
||||||
|
|
||||||
|
|||||||
@ -46,10 +46,8 @@ TEST_F(VideoProcessorIntegrationTest, Process0PercentPacketLossH264) {
|
|||||||
// Metrics for rate control.
|
// Metrics for rate control.
|
||||||
RateControlMetrics rc_metrics[1];
|
RateControlMetrics rc_metrics[1];
|
||||||
SetRateControlMetrics(rc_metrics, 0, 2, 60, 20, 10, 20, 0, 1);
|
SetRateControlMetrics(rc_metrics, 0, 2, 60, 20, 10, 20, 0, 1);
|
||||||
ProcessFramesAndVerify(quality_metrics,
|
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
||||||
rate_profile,
|
rc_metrics, nullptr /* visualization_params */);
|
||||||
process_settings,
|
|
||||||
rc_metrics);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // defined(WEBRTC_VIDEOPROCESSOR_H264_TESTS)
|
#endif // defined(WEBRTC_VIDEOPROCESSOR_H264_TESTS)
|
||||||
@ -78,7 +76,7 @@ TEST_F(VideoProcessorIntegrationTest, Process0PercentPacketLossVP9) {
|
|||||||
RateControlMetrics rc_metrics[1];
|
RateControlMetrics rc_metrics[1];
|
||||||
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 20, 0, 1);
|
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 20, 0, 1);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
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
|
// 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];
|
RateControlMetrics rc_metrics[1];
|
||||||
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 20, 0, 1);
|
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 20, 0, 1);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
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):
|
// 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, 1, 2, 0, 20, 20, 60, 0, 0);
|
||||||
SetRateControlMetrics(rc_metrics, 2, 0, 0, 25, 20, 40, 0, 0);
|
SetRateControlMetrics(rc_metrics, 2, 0, 0, 25, 20, 40, 0, 0);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
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.
|
// 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, 1, 10, 0, 40, 10, 30, 0, 0);
|
||||||
SetRateControlMetrics(rc_metrics, 2, 5, 0, 30, 5, 20, 0, 0);
|
SetRateControlMetrics(rc_metrics, 2, 5, 0, 30, 5, 20, 0, 0);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
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).
|
// 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];
|
RateControlMetrics rc_metrics[1];
|
||||||
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 20, 0, 1);
|
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 20, 0, 1);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
||||||
rc_metrics);
|
rc_metrics, nullptr /* visualization_params */);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run with no packet loss, at low bitrate.
|
// Run with no packet loss, at low bitrate.
|
||||||
@ -207,7 +205,7 @@ TEST_F(VideoProcessorIntegrationTest,
|
|||||||
RateControlMetrics rc_metrics[1];
|
RateControlMetrics rc_metrics[1];
|
||||||
SetRateControlMetrics(rc_metrics, 0, 228, 70, 160, 15, 80, 1, 1);
|
SetRateControlMetrics(rc_metrics, 0, 228, 70, 160, 15, 80, 1, 1);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
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
|
// TODO(marpan): Add temporal layer test for VP9, once changes are in
|
||||||
@ -235,7 +233,7 @@ TEST_F(VideoProcessorIntegrationTest, ProcessZeroPacketLoss) {
|
|||||||
RateControlMetrics rc_metrics[1];
|
RateControlMetrics rc_metrics[1];
|
||||||
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0, 1);
|
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0, 1);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
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
|
// 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];
|
RateControlMetrics rc_metrics[1];
|
||||||
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0, 1);
|
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0, 1);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
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.
|
// 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];
|
RateControlMetrics rc_metrics[1];
|
||||||
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0, 1);
|
SetRateControlMetrics(rc_metrics, 0, 0, 40, 20, 10, 15, 0, 1);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
||||||
rc_metrics);
|
rc_metrics, nullptr /* visualization_params */);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !defined(WEBRTC_IOS)
|
#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, 1, 0, 0, 25, 20, 10, 0, 0);
|
||||||
SetRateControlMetrics(rc_metrics, 2, 0, 0, 25, 15, 10, 0, 0);
|
SetRateControlMetrics(rc_metrics, 2, 0, 0, 25, 15, 10, 0, 0);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
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.
|
// 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, 1, 10, 0, 25, 10, 35, 0, 0);
|
||||||
SetRateControlMetrics(rc_metrics, 2, 0, 0, 20, 10, 15, 0, 0);
|
SetRateControlMetrics(rc_metrics, 2, 0, 0, 20, 10, 15, 0, 0);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
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
|
// 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, 0, 0, 20, 30, 10, 10, 0, 1);
|
||||||
SetRateControlMetrics(rc_metrics, 1, 0, 0, 30, 15, 10, 0, 0);
|
SetRateControlMetrics(rc_metrics, 1, 0, 0, 30, 15, 10, 0, 0);
|
||||||
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
ProcessFramesAndVerify(quality_metrics, rate_profile, process_settings,
|
||||||
rc_metrics);
|
rc_metrics, nullptr /* visualization_params */);
|
||||||
}
|
}
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#if defined(WEBRTC_ANDROID)
|
#if defined(WEBRTC_ANDROID)
|
||||||
#include "webrtc/modules/video_coding/codecs/test/android_test_initializer.h"
|
#include "webrtc/modules/video_coding/codecs/test/android_test_initializer.h"
|
||||||
@ -26,6 +27,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "webrtc/base/checks.h"
|
#include "webrtc/base/checks.h"
|
||||||
|
#include "webrtc/base/file.h"
|
||||||
#include "webrtc/media/engine/webrtcvideodecoderfactory.h"
|
#include "webrtc/media/engine/webrtcvideodecoderfactory.h"
|
||||||
#include "webrtc/media/engine/webrtcvideoencoderfactory.h"
|
#include "webrtc/media/engine/webrtcvideoencoderfactory.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/h264/include/h264.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/test/videoprocessor.h"
|
||||||
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.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/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/codecs/vp9/include/vp9.h"
|
||||||
#include "webrtc/modules/video_coding/include/video_codec_interface.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/include/video_coding.h"
|
||||||
|
#include "webrtc/modules/video_coding/utility/ivf_file_writer.h"
|
||||||
#include "webrtc/test/gtest.h"
|
#include "webrtc/test/gtest.h"
|
||||||
#include "webrtc/test/testsupport/fileutils.h"
|
#include "webrtc/test/testsupport/fileutils.h"
|
||||||
#include "webrtc/test/testsupport/frame_reader.h"
|
#include "webrtc/test/testsupport/frame_reader.h"
|
||||||
@ -110,6 +112,13 @@ struct RateControlMetrics {
|
|||||||
int num_key_frames;
|
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)
|
#if !defined(WEBRTC_IOS)
|
||||||
const int kNumFramesShort = 100;
|
const int kNumFramesShort = 100;
|
||||||
#endif
|
#endif
|
||||||
@ -143,7 +152,8 @@ class VideoProcessorIntegrationTest : public testing::Test {
|
|||||||
}
|
}
|
||||||
virtual ~VideoProcessorIntegrationTest() = default;
|
virtual ~VideoProcessorIntegrationTest() = default;
|
||||||
|
|
||||||
void SetUpCodecConfig(const CodecConfigPars& process) {
|
void SetUpCodecConfig(const CodecConfigPars& process,
|
||||||
|
const VisualizationParams* visualization_params) {
|
||||||
if (process.hw_codec) {
|
if (process.hw_codec) {
|
||||||
#if defined(WEBRTC_VIDEOPROCESSOR_INTEGRATIONTEST_HW_CODECS_ENABLED)
|
#if defined(WEBRTC_VIDEOPROCESSOR_INTEGRATIONTEST_HW_CODECS_ENABLED)
|
||||||
#if defined(WEBRTC_ANDROID)
|
#if defined(WEBRTC_ANDROID)
|
||||||
@ -175,7 +185,7 @@ class VideoProcessorIntegrationTest : public testing::Test {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#elif defined(WEBRTC_IOS)
|
#elif defined(WEBRTC_IOS)
|
||||||
RTC_DCHECK_EQ(kVideoCodecH264, process.codec_type)
|
ASSERT_EQ(kVideoCodecH264, process.codec_type)
|
||||||
<< "iOS HW codecs only support H264.";
|
<< "iOS HW codecs only support H264.";
|
||||||
encoder_.reset(new H264VideoToolboxEncoder(
|
encoder_.reset(new H264VideoToolboxEncoder(
|
||||||
cricket::VideoCodec(cricket::kH264CodecName)));
|
cricket::VideoCodec(cricket::kH264CodecName)));
|
||||||
@ -184,8 +194,8 @@ class VideoProcessorIntegrationTest : public testing::Test {
|
|||||||
RTC_NOTREACHED() << "Only support HW codecs on Android and iOS.";
|
RTC_NOTREACHED() << "Only support HW codecs on Android and iOS.";
|
||||||
#endif
|
#endif
|
||||||
#endif // WEBRTC_VIDEOPROCESSOR_INTEGRATIONTEST_HW_CODECS_ENABLED
|
#endif // WEBRTC_VIDEOPROCESSOR_INTEGRATIONTEST_HW_CODECS_ENABLED
|
||||||
RTC_DCHECK(encoder_) << "HW encoder not successfully created.";
|
RTC_CHECK(encoder_) << "HW encoder not successfully created.";
|
||||||
RTC_DCHECK(decoder_) << "HW decoder not successfully created.";
|
RTC_CHECK(decoder_) << "HW decoder not successfully created.";
|
||||||
} else {
|
} else {
|
||||||
// SW codecs.
|
// SW codecs.
|
||||||
switch (process.codec_type) {
|
switch (process.codec_type) {
|
||||||
@ -265,19 +275,54 @@ class VideoProcessorIntegrationTest : public testing::Test {
|
|||||||
RTC_NOTREACHED();
|
RTC_NOTREACHED();
|
||||||
break;
|
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_.input_filename, config_.codec_settings->width,
|
||||||
config_.codec_settings->height));
|
config_.codec_settings->height));
|
||||||
frame_writer_.reset(new test::FrameWriterImpl(
|
analysis_frame_writer_.reset(new test::YuvFrameWriterImpl(
|
||||||
config_.output_filename, config_.frame_length_in_bytes));
|
config_.output_filename, config_.codec_settings->width,
|
||||||
RTC_CHECK(frame_reader_->Init());
|
config_.codec_settings->height));
|
||||||
RTC_CHECK(frame_writer_->Init());
|
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_manipulator_.reset(new test::PacketManipulatorImpl(
|
||||||
&packet_reader_, config_.networking_config, config_.verbose));
|
&packet_reader_, config_.networking_config, config_.verbose));
|
||||||
processor_.reset(new test::VideoProcessorImpl(
|
processor_.reset(new test::VideoProcessorImpl(
|
||||||
encoder_.get(), decoder_.get(), frame_reader_.get(),
|
encoder_.get(), decoder_.get(), analysis_frame_reader_.get(),
|
||||||
frame_writer_.get(), packet_manipulator_.get(), config_, &stats_));
|
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());
|
RTC_CHECK(processor_->Init());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,12 +508,14 @@ class VideoProcessorIntegrationTest : public testing::Test {
|
|||||||
void ProcessFramesAndVerify(QualityMetrics quality_metrics,
|
void ProcessFramesAndVerify(QualityMetrics quality_metrics,
|
||||||
RateProfile rate_profile,
|
RateProfile rate_profile,
|
||||||
CodecConfigPars process,
|
CodecConfigPars process,
|
||||||
RateControlMetrics* rc_metrics) {
|
RateControlMetrics* rc_metrics,
|
||||||
|
const VisualizationParams* visualization_params) {
|
||||||
// Codec/config settings.
|
// Codec/config settings.
|
||||||
start_bitrate_ = rate_profile.target_bit_rate[0];
|
start_bitrate_ = rate_profile.target_bit_rate[0];
|
||||||
|
start_frame_rate_ = rate_profile.input_frame_rate[0];
|
||||||
packet_loss_ = process.packet_loss;
|
packet_loss_ = process.packet_loss;
|
||||||
num_temporal_layers_ = process.num_temporal_layers;
|
num_temporal_layers_ = process.num_temporal_layers;
|
||||||
SetUpCodecConfig(process);
|
SetUpCodecConfig(process, visualization_params);
|
||||||
// Update the layers and the codec with the initial rates.
|
// Update the layers and the codec with the initial rates.
|
||||||
bit_rate_ = rate_profile.target_bit_rate[0];
|
bit_rate_ = rate_profile.target_bit_rate[0];
|
||||||
frame_rate_ = rate_profile.input_frame_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, encoder_->Release());
|
||||||
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Release());
|
EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Release());
|
||||||
|
|
||||||
// Close the files before we start using them for SSIM/PSNR calculations.
|
// Close the analysis files before we use them for SSIM/PSNR calculations.
|
||||||
frame_reader_->Close();
|
analysis_frame_reader_->Close();
|
||||||
frame_writer_->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;
|
test::QualityMetricsResult psnr_result, ssim_result;
|
||||||
EXPECT_EQ(0, test::I420MetricsFromFiles(config_.input_filename.c_str(),
|
EXPECT_EQ(0, test::I420MetricsFromFiles(config_.input_filename.c_str(),
|
||||||
config_.output_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(psnr_result.min, quality_metrics.minimum_min_psnr);
|
||||||
EXPECT_GT(ssim_result.average, quality_metrics.minimum_avg_ssim);
|
EXPECT_GT(ssim_result.average, quality_metrics.minimum_avg_ssim);
|
||||||
EXPECT_GT(ssim_result.min, quality_metrics.minimum_min_ssim);
|
EXPECT_GT(ssim_result.min, quality_metrics.minimum_min_ssim);
|
||||||
|
|
||||||
|
// Remove analysis file.
|
||||||
if (remove(config_.output_filename.c_str()) < 0) {
|
if (remove(config_.output_filename.c_str()) < 0) {
|
||||||
fprintf(stderr, "Failed to remove temporary file!\n");
|
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,
|
static void SetCodecParameters(CodecConfigPars* process_settings,
|
||||||
VideoCodecType codec_type,
|
VideoCodecType codec_type,
|
||||||
bool hw_codec,
|
bool hw_codec,
|
||||||
@ -629,6 +678,17 @@ class VideoProcessorIntegrationTest : public testing::Test {
|
|||||||
quality_metrics->minimum_min_ssim = minimum_min_ssim;
|
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,
|
static void SetRateControlMetrics(RateControlMetrics* rc_metrics,
|
||||||
int update_index,
|
int update_index,
|
||||||
int max_num_dropped_frames,
|
int max_num_dropped_frames,
|
||||||
@ -650,20 +710,27 @@ class VideoProcessorIntegrationTest : public testing::Test {
|
|||||||
rc_metrics[update_index].num_key_frames = num_key_frames;
|
rc_metrics[update_index].num_key_frames = num_key_frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Codecs.
|
||||||
std::unique_ptr<VideoEncoder> encoder_;
|
std::unique_ptr<VideoEncoder> encoder_;
|
||||||
std::unique_ptr<cricket::WebRtcVideoEncoderFactory> external_encoder_factory_;
|
std::unique_ptr<cricket::WebRtcVideoEncoderFactory> external_encoder_factory_;
|
||||||
std::unique_ptr<VideoDecoder> decoder_;
|
std::unique_ptr<VideoDecoder> decoder_;
|
||||||
std::unique_ptr<cricket::WebRtcVideoDecoderFactory> external_decoder_factory_;
|
std::unique_ptr<cricket::WebRtcVideoDecoderFactory> external_decoder_factory_;
|
||||||
std::unique_ptr<test::FrameReader> frame_reader_;
|
VideoCodec codec_settings_;
|
||||||
std::unique_ptr<test::FrameWriter> frame_writer_;
|
|
||||||
|
// Helper objects.
|
||||||
|
std::unique_ptr<test::FrameReader> analysis_frame_reader_;
|
||||||
|
std::unique_ptr<test::FrameWriter> analysis_frame_writer_;
|
||||||
test::PacketReader packet_reader_;
|
test::PacketReader packet_reader_;
|
||||||
std::unique_ptr<test::PacketManipulator> packet_manipulator_;
|
std::unique_ptr<test::PacketManipulator> packet_manipulator_;
|
||||||
test::Stats stats_;
|
test::Stats stats_;
|
||||||
test::TestConfig config_;
|
test::TestConfig config_;
|
||||||
VideoCodec codec_settings_;
|
|
||||||
// Must be destroyed before |encoder_| and |decoder_|.
|
// Must be destroyed before |encoder_| and |decoder_|.
|
||||||
std::unique_ptr<test::VideoProcessor> processor_;
|
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.
|
// Quantities defined/updated for every encoder rate update.
|
||||||
// Some quantities defined per temporal layer (at most 3 layers in this test).
|
// 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_;
|
float sum_key_frame_size_mismatch_;
|
||||||
int num_key_frames_;
|
int num_key_frames_;
|
||||||
float start_bitrate_;
|
float start_bitrate_;
|
||||||
|
int start_frame_rate_;
|
||||||
|
|
||||||
// Codec and network settings.
|
// Codec and network settings.
|
||||||
float packet_loss_;
|
float packet_loss_;
|
||||||
|
|||||||
@ -69,7 +69,9 @@ TEST_F(VideoProcessorTest, Init) {
|
|||||||
ExpectInit();
|
ExpectInit();
|
||||||
VideoProcessorImpl video_processor(
|
VideoProcessorImpl video_processor(
|
||||||
&encoder_mock_, &decoder_mock_, &frame_reader_mock_, &frame_writer_mock_,
|
&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());
|
ASSERT_TRUE(video_processor.Init());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,7 +84,9 @@ TEST_F(VideoProcessorTest, ProcessFrame) {
|
|||||||
// be more than initialized...
|
// be more than initialized...
|
||||||
VideoProcessorImpl video_processor(
|
VideoProcessorImpl video_processor(
|
||||||
&encoder_mock_, &decoder_mock_, &frame_reader_mock_, &frame_writer_mock_,
|
&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());
|
ASSERT_TRUE(video_processor.Init());
|
||||||
video_processor.ProcessFrame(0);
|
video_processor.ProcessFrame(0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -490,11 +490,12 @@ int main(int argc, char* argv[]) {
|
|||||||
webrtc::VP8Encoder* encoder = webrtc::VP8Encoder::Create();
|
webrtc::VP8Encoder* encoder = webrtc::VP8Encoder::Create();
|
||||||
webrtc::VP8Decoder* decoder = webrtc::VP8Decoder::Create();
|
webrtc::VP8Decoder* decoder = webrtc::VP8Decoder::Create();
|
||||||
webrtc::test::Stats stats;
|
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->width,
|
||||||
config.codec_settings->height);
|
config.codec_settings->height);
|
||||||
webrtc::test::FrameWriterImpl frame_writer(config.output_filename,
|
webrtc::test::YuvFrameWriterImpl frame_writer(config.output_filename,
|
||||||
config.frame_length_in_bytes);
|
config.codec_settings->width,
|
||||||
|
config.codec_settings->height);
|
||||||
frame_reader.Init();
|
frame_reader.Init();
|
||||||
frame_writer.Init();
|
frame_writer.Init();
|
||||||
webrtc::test::PacketReader packet_reader;
|
webrtc::test::PacketReader packet_reader;
|
||||||
@ -507,9 +508,11 @@ int main(int argc, char* argv[]) {
|
|||||||
packet_manipulator.InitializeRandomSeed(time(NULL));
|
packet_manipulator.InitializeRandomSeed(time(NULL));
|
||||||
}
|
}
|
||||||
webrtc::test::VideoProcessor* processor =
|
webrtc::test::VideoProcessor* processor =
|
||||||
new webrtc::test::VideoProcessorImpl(encoder, decoder, &frame_reader,
|
new webrtc::test::VideoProcessorImpl(
|
||||||
&frame_writer, &packet_manipulator,
|
encoder, decoder, &frame_reader, &frame_writer, &packet_manipulator,
|
||||||
config, &stats);
|
config, &stats, nullptr /* source_frame_writer */,
|
||||||
|
nullptr /* encoded_frame_writer */,
|
||||||
|
nullptr /* decoded_frame_writer */);
|
||||||
processor->Init();
|
processor->Init();
|
||||||
|
|
||||||
int frame_number = 0;
|
int frame_number = 0;
|
||||||
|
|||||||
@ -129,14 +129,15 @@ rtc_source_set("video_test_support") {
|
|||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
sources = [
|
sources = [
|
||||||
"testsupport/frame_reader.cc",
|
|
||||||
"testsupport/frame_reader.h",
|
"testsupport/frame_reader.h",
|
||||||
"testsupport/frame_writer.cc",
|
|
||||||
"testsupport/frame_writer.h",
|
"testsupport/frame_writer.h",
|
||||||
"testsupport/metrics/video_metrics.cc",
|
"testsupport/metrics/video_metrics.cc",
|
||||||
"testsupport/metrics/video_metrics.h",
|
"testsupport/metrics/video_metrics.h",
|
||||||
"testsupport/mock/mock_frame_reader.h",
|
"testsupport/mock/mock_frame_reader.h",
|
||||||
"testsupport/mock/mock_frame_writer.h",
|
"testsupport/mock/mock_frame_writer.h",
|
||||||
|
"testsupport/y4m_frame_writer.cc",
|
||||||
|
"testsupport/yuv_frame_reader.cc",
|
||||||
|
"testsupport/yuv_frame_writer.cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
@ -259,12 +260,13 @@ rtc_test("test_support_unittests") {
|
|||||||
"rtp_file_reader_unittest.cc",
|
"rtp_file_reader_unittest.cc",
|
||||||
"rtp_file_writer_unittest.cc",
|
"rtp_file_writer_unittest.cc",
|
||||||
"testsupport/always_passing_unittest.cc",
|
"testsupport/always_passing_unittest.cc",
|
||||||
"testsupport/frame_reader_unittest.cc",
|
|
||||||
"testsupport/frame_writer_unittest.cc",
|
|
||||||
"testsupport/isolated_output_unittest.cc",
|
"testsupport/isolated_output_unittest.cc",
|
||||||
"testsupport/metrics/video_metrics_unittest.cc",
|
"testsupport/metrics/video_metrics_unittest.cc",
|
||||||
"testsupport/packet_reader_unittest.cc",
|
"testsupport/packet_reader_unittest.cc",
|
||||||
"testsupport/perf_test_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.
|
# TODO(jschuh): Bug 1348: fix this warning.
|
||||||
|
|||||||
@ -46,14 +46,14 @@ class FrameReader {
|
|||||||
virtual int NumberOfFrames() = 0;
|
virtual int NumberOfFrames() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FrameReaderImpl : public FrameReader {
|
class YuvFrameReaderImpl : public FrameReader {
|
||||||
public:
|
public:
|
||||||
// Creates a file handler. The input file is assumed to exist and be readable.
|
// Creates a file handler. The input file is assumed to exist and be readable.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// input_filename The file to read from.
|
// input_filename The file to read from.
|
||||||
// width, height Size of each frame to read.
|
// width, height Size of each frame to read.
|
||||||
FrameReaderImpl(std::string input_filename, int width, int height);
|
YuvFrameReaderImpl(std::string input_filename, int width, int height);
|
||||||
~FrameReaderImpl() override;
|
~YuvFrameReaderImpl() override;
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
rtc::scoped_refptr<I420Buffer> ReadFrame() override;
|
rtc::scoped_refptr<I420Buffer> ReadFrame() override;
|
||||||
void Close() override;
|
void Close() override;
|
||||||
@ -61,10 +61,10 @@ class FrameReaderImpl : public FrameReader {
|
|||||||
int NumberOfFrames() override;
|
int NumberOfFrames() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string input_filename_;
|
const std::string input_filename_;
|
||||||
size_t frame_length_in_bytes_;
|
size_t frame_length_in_bytes_;
|
||||||
int width_;
|
const int width_;
|
||||||
int height_;
|
const int height_;
|
||||||
int number_of_frames_;
|
int number_of_frames_;
|
||||||
FILE* input_file_;
|
FILE* input_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
|
|
||||||
@ -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
|
|
||||||
@ -42,28 +42,46 @@ class FrameWriter {
|
|||||||
virtual size_t FrameLength() = 0;
|
virtual size_t FrameLength() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FrameWriterImpl : public FrameWriter {
|
// Writes raw I420 frames in sequence.
|
||||||
|
class YuvFrameWriterImpl : public FrameWriter {
|
||||||
public:
|
public:
|
||||||
// Creates a file handler. The input file is assumed to exist and be readable
|
// Creates a file handler. The input file is assumed to exist and be readable
|
||||||
// and the output file must be writable.
|
// and the output file must be writable.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// output_filename The file to write. Will be overwritten if already
|
// output_filename The file to write. Will be overwritten if already
|
||||||
// existing.
|
// existing.
|
||||||
// frame_length_in_bytes The size of each frame.
|
// width, height Size of each frame to read.
|
||||||
// For YUV: 3*width*height/2
|
YuvFrameWriterImpl(std::string output_filename, int width, int height);
|
||||||
FrameWriterImpl(std::string output_filename, size_t frame_length_in_bytes);
|
~YuvFrameWriterImpl() override;
|
||||||
~FrameWriterImpl() override;
|
|
||||||
bool Init() override;
|
bool Init() override;
|
||||||
bool WriteFrame(uint8_t* frame_buffer) override;
|
bool WriteFrame(uint8_t* frame_buffer) override;
|
||||||
void Close() override;
|
void Close() override;
|
||||||
size_t FrameLength() override;
|
size_t FrameLength() override;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
std::string output_filename_;
|
const std::string output_filename_;
|
||||||
size_t frame_length_in_bytes_;
|
size_t frame_length_in_bytes_;
|
||||||
|
const int width_;
|
||||||
|
const int height_;
|
||||||
FILE* output_file_;
|
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 test
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
||||||
|
|||||||
@ -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
|
|
||||||
56
webrtc/test/testsupport/y4m_frame_writer.cc
Normal file
56
webrtc/test/testsupport/y4m_frame_writer.cc
Normal 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
|
||||||
77
webrtc/test/testsupport/y4m_frame_writer_unittest.cc
Normal file
77
webrtc/test/testsupport/y4m_frame_writer_unittest.cc
Normal 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
|
||||||
@ -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
|
* 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
|
* 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 "webrtc/test/testsupport/frame_reader.h"
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include "webrtc/api/video/i420_buffer.h"
|
#include "webrtc/api/video/i420_buffer.h"
|
||||||
#include "webrtc/test/frame_utils.h"
|
#include "webrtc/test/frame_utils.h"
|
||||||
#include "webrtc/test/testsupport/fileutils.h"
|
#include "webrtc/test/testsupport/fileutils.h"
|
||||||
@ -19,28 +17,31 @@
|
|||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
FrameReaderImpl::FrameReaderImpl(std::string input_filename,
|
YuvFrameReaderImpl::YuvFrameReaderImpl(std::string input_filename,
|
||||||
int width, int height)
|
int width,
|
||||||
|
int height)
|
||||||
: input_filename_(input_filename),
|
: input_filename_(input_filename),
|
||||||
width_(width), height_(height),
|
frame_length_in_bytes_(0),
|
||||||
input_file_(NULL) {
|
width_(width),
|
||||||
}
|
height_(height),
|
||||||
|
number_of_frames_(-1),
|
||||||
|
input_file_(nullptr) {}
|
||||||
|
|
||||||
FrameReaderImpl::~FrameReaderImpl() {
|
YuvFrameReaderImpl::~YuvFrameReaderImpl() {
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FrameReaderImpl::Init() {
|
bool YuvFrameReaderImpl::Init() {
|
||||||
if (width_ <= 0 || height_ <= 0) {
|
if (width_ <= 0 || height_ <= 0) {
|
||||||
fprintf(stderr, "Frame width and height must be >0, was %d x %d\n",
|
fprintf(stderr, "Frame width and height must be >0, was %d x %d\n", width_,
|
||||||
width_, height_);
|
height_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
frame_length_in_bytes_ =
|
frame_length_in_bytes_ =
|
||||||
width_ * height_ + 2 * ((width_ + 1) / 2) * ((height_ + 1) / 2);
|
width_ * height_ + 2 * ((width_ + 1) / 2) * ((height_ + 1) / 2);
|
||||||
|
|
||||||
input_file_ = fopen(input_filename_.c_str(), "rb");
|
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",
|
fprintf(stderr, "Couldn't open input file for reading: %s\n",
|
||||||
input_filename_.c_str());
|
input_filename_.c_str());
|
||||||
return false;
|
return false;
|
||||||
@ -51,21 +52,15 @@ bool FrameReaderImpl::Init() {
|
|||||||
fprintf(stderr, "Found empty file: %s\n", input_filename_.c_str());
|
fprintf(stderr, "Found empty file: %s\n", input_filename_.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
number_of_frames_ = static_cast<int>(source_file_size /
|
number_of_frames_ =
|
||||||
frame_length_in_bytes_);
|
static_cast<int>(source_file_size / frame_length_in_bytes_);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FrameReaderImpl::Close() {
|
rtc::scoped_refptr<I420Buffer> YuvFrameReaderImpl::ReadFrame() {
|
||||||
if (input_file_ != NULL) {
|
if (input_file_ == nullptr) {
|
||||||
fclose(input_file_);
|
fprintf(stderr,
|
||||||
input_file_ = NULL;
|
"YuvFrameReaderImpl is not initialized (input file is NULL)\n");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rtc::scoped_refptr<I420Buffer> FrameReaderImpl::ReadFrame() {
|
|
||||||
if (input_file_ == NULL) {
|
|
||||||
fprintf(stderr, "FrameReader is not initialized (input file is NULL)\n");
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
rtc::scoped_refptr<I420Buffer> buffer(
|
rtc::scoped_refptr<I420Buffer> buffer(
|
||||||
@ -77,8 +72,20 @@ rtc::scoped_refptr<I420Buffer> FrameReaderImpl::ReadFrame() {
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FrameReaderImpl::FrameLength() { return frame_length_in_bytes_; }
|
void YuvFrameReaderImpl::Close() {
|
||||||
int FrameReaderImpl::NumberOfFrames() { return number_of_frames_; }
|
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 test
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
83
webrtc/test/testsupport/yuv_frame_reader_unittest.cc
Normal file
83
webrtc/test/testsupport/yuv_frame_reader_unittest.cc
Normal 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
|
||||||
77
webrtc/test/testsupport/yuv_frame_writer.cc
Normal file
77
webrtc/test/testsupport/yuv_frame_writer.cc
Normal 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
|
||||||
68
webrtc/test/testsupport/yuv_frame_writer_unittest.cc
Normal file
68
webrtc/test/testsupport/yuv_frame_writer_unittest.cc
Normal 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
|
||||||
Reference in New Issue
Block a user