Add WriteVideoToFile to video_file_reader.

The function checks the file extension to determine YUV or Y4M format.

Also adds a flag aligned_output_file to compare_videos.py, which allows
saving the aligned reference video to a file.

Bug: webrtc:9642
Change-Id: Ia59f5c123a1e41104756eb6b235b6581c4ffbd77
Reviewed-on: https://webrtc-review.googlesource.com/99503
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Patrik Höglund <phoglund@webrtc.org>
Commit-Queue: Paulina Hensman <phensman@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24787}
This commit is contained in:
Paulina Hensman 2018-09-14 11:32:00 +02:00 committed by Commit Bot
parent 5773ad3bc8
commit b671d46f91
8 changed files with 267 additions and 3 deletions

View file

@ -73,6 +73,20 @@ rtc_static_library("video_file_reader") {
]
}
rtc_static_library("video_file_writer") {
sources = [
"video_file_writer.cc",
"video_file_writer.h",
]
deps = [
":video_file_reader",
"../api/video:video_frame",
"../api/video:video_frame_i420",
"../rtc_base:rtc_base_approved",
"//third_party/abseil-cpp/absl/types:optional",
]
}
rtc_static_library("video_quality_analysis") {
sources = [
"frame_analyzer/video_quality_analysis.cc",
@ -101,6 +115,7 @@ rtc_executable("frame_analyzer") {
deps = [
":command_line_parser",
":video_file_reader",
":video_file_writer",
":video_quality_analysis",
"../rtc_base:stringutils",
"../test:perf_test",
@ -343,6 +358,7 @@ if (rtc_include_tests) {
"sanitizers_unittest.cc",
"simple_command_line_parser_unittest.cc",
"video_file_reader_unittest.cc",
"video_file_writer_unittest.cc",
]
if (!build_with_chromium && is_clang) {
@ -355,6 +371,7 @@ if (rtc_include_tests) {
":frame_editing_lib",
":reference_less_video_analysis_lib",
":video_file_reader",
":video_file_writer",
":video_quality_analysis",
"../common_video:common_video",
"../rtc_base",

View file

@ -36,6 +36,8 @@ def _ParseArgs():
'video (YUV).'))
parser.add_option('--frame_analyzer', type='string',
help='Path to the frame analyzer executable.')
parser.add_option('--aligned_output_file', type='string',
help='Path for output aligned YUV or Y4M file.')
parser.add_option('--barcode_decoder', type='string',
help=('Path to the barcode decoder script. By default, we '
'will assume we can find it in barcode_tools/'
@ -127,7 +129,7 @@ def main():
"""The main function.
A simple invocation is:
./webrtc/rtc_tools/barcode_tools/compare_videos.py
./webrtc/rtc_tools/compare_videos.py
--ref_video=<path_and_name_of_reference_video>
--test_video=<path_and_name_of_test_video>
--frame_analyzer=<path_and_name_of_the_frame_analyzer_executable>
@ -165,6 +167,8 @@ def main():
]
if options.chartjson_result_file:
cmd.append('--chartjson_result_file=%s' % options.chartjson_result_file)
if options.aligned_output_file:
cmd.append('--aligned_output_file=%s' % options.aligned_output_file)
frame_analyzer = subprocess.Popen(cmd, stdin=_DevNull(),
stdout=sys.stdout, stderr=sys.stderr)
frame_analyzer.wait()

View file

@ -20,6 +20,7 @@
#include "rtc_tools/frame_analyzer/video_temporal_aligner.h"
#include "rtc_tools/simple_command_line_parser.h"
#include "rtc_tools/video_file_reader.h"
#include "rtc_tools/video_file_writer.h"
#include "test/testsupport/perf_test.h"
/*
@ -56,6 +57,9 @@ int main(int argc, char* argv[]) {
" Default: test_file.yuv\n"
" - chartjson_result_file: Where to store perf result in chartjson"
" format. If not present, no perf result will be stored."
" Default: None\n"
" - aligned_output_file: Where to write aligned YUV/Y4M output file."
" If not present, no file will be written."
" Default: None\n";
webrtc::test::CommandLineParser parser;
@ -69,6 +73,7 @@ int main(int argc, char* argv[]) {
parser.SetFlag("label", "MY_TEST");
parser.SetFlag("reference_file", "ref.yuv");
parser.SetFlag("test_file", "test.yuv");
parser.SetFlag("aligned_output_file", "");
parser.SetFlag("chartjson_result_file", "");
parser.SetFlag("help", "false");
@ -128,6 +133,14 @@ int main(int argc, char* argv[]) {
if (!chartjson_result_file.empty()) {
webrtc::test::WritePerfResults(chartjson_result_file);
}
std::string aligned_output_file = parser.GetFlag("aligned_output_file");
if (!aligned_output_file.empty()) {
rtc::scoped_refptr<webrtc::test::Video> reordered_video =
webrtc::test::GenerateAlignedReferenceVideo(reference_video,
matching_indices);
webrtc::test::WriteVideoToFile(reordered_video, aligned_output_file,
/*fps=*/30);
}
return 0;
}

View file

@ -221,8 +221,14 @@ rtc::scoped_refptr<Video> ReorderVideo(const rtc::scoped_refptr<Video>& video,
rtc::scoped_refptr<Video> GenerateAlignedReferenceVideo(
const rtc::scoped_refptr<Video>& reference_video,
const rtc::scoped_refptr<Video>& test_video) {
return ReorderVideo(new LoopingVideo(reference_video),
FindMatchingFrameIndices(reference_video, test_video));
return GenerateAlignedReferenceVideo(
reference_video, FindMatchingFrameIndices(reference_video, test_video));
}
rtc::scoped_refptr<Video> GenerateAlignedReferenceVideo(
const rtc::scoped_refptr<Video>& reference_video,
const std::vector<size_t>& indices) {
return ReorderVideo(new LoopingVideo(reference_video), indices);
}
} // namespace test

View file

@ -47,6 +47,11 @@ rtc::scoped_refptr<Video> GenerateAlignedReferenceVideo(
const rtc::scoped_refptr<Video>& reference_video,
const rtc::scoped_refptr<Video>& test_video);
// As above, but using precalculated indices.
rtc::scoped_refptr<Video> GenerateAlignedReferenceVideo(
const rtc::scoped_refptr<Video>& reference_video,
const std::vector<size_t>& indices);
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,66 @@
/*
* Copyright (c) 2018 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 "rtc_tools/video_file_writer.h"
#include <cmath>
#include <string>
#include "api/video/i420_buffer.h"
#include "rtc_base/logging.h"
#include "rtc_base/refcountedobject.h"
namespace webrtc {
namespace test {
void WriteVideoToFile(const rtc::scoped_refptr<Video>& video,
const std::string& file_name,
int fps) {
FILE* output_file = fopen(file_name.c_str(), "wb");
if (output_file == nullptr) {
RTC_LOG(LS_ERROR) << "Could not open file for writing: " << file_name;
return;
}
bool isY4m = rtc::ends_with(file_name.c_str(), ".y4m");
if (isY4m) {
fprintf(output_file, "YUV4MPEG2 W%d H%d F%d:1 C420\n", video->width(),
video->height(), fps);
}
for (size_t i = 0; i < video->number_of_frames(); ++i) {
if (isY4m) {
std::string frame = "FRAME\n";
fwrite(frame.c_str(), 1, 6, output_file);
}
rtc::scoped_refptr<I420BufferInterface> buffer = video->GetFrame(i);
const uint8_t* data_y = buffer->DataY();
int stride = buffer->StrideY();
for (int i = 0; i < video->height(); ++i) {
fwrite(data_y + i * stride, /*size=*/1, stride, output_file);
}
const uint8_t* data_u = buffer->DataU();
stride = buffer->StrideU();
for (int i = 0; i < buffer->ChromaHeight(); ++i) {
fwrite(data_u + i * stride, /*size=*/1, stride, output_file);
}
const uint8_t* data_v = buffer->DataV();
stride = buffer->StrideV();
for (int i = 0; i < buffer->ChromaHeight(); ++i) {
fwrite(data_v + i * stride, /*size=*/1, stride, output_file);
}
}
if (ferror(output_file) != 0) {
RTC_LOG(LS_ERROR) << "Error writing to file " << file_name;
}
fclose(output_file);
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef RTC_TOOLS_VIDEO_FILE_WRITER_H_
#define RTC_TOOLS_VIDEO_FILE_WRITER_H_
#include <cstdio>
#include <string>
#include "rtc_base/refcount.h"
#include "rtc_tools/video_file_reader.h"
namespace webrtc {
namespace test {
// Writes video to file, determining YUV or Y4M format from the file extension.
void WriteVideoToFile(const rtc::scoped_refptr<Video>& video,
const std::string& file_name,
int fps);
} // namespace test
} // namespace webrtc
#endif // RTC_TOOLS_VIDEO_FILE_WRITER_H_

View file

@ -0,0 +1,123 @@
/*
* Copyright (c) 2018 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 <string>
#include "rtc_base/refcountedobject.h"
#include "rtc_tools/video_file_reader.h"
#include "rtc_tools/video_file_writer.h"
#include "test/gtest.h"
#include "test/testsupport/fileutils.h"
namespace webrtc {
namespace test {
class VideoFileWriterTest : public ::testing::Test {
public:
void SetUp() override {
const std::string filename =
webrtc::test::OutputPath() + "test_video_file.y4m";
// Create simple test video of size 6x4.
FILE* file = fopen(filename.c_str(), "wb");
ASSERT_TRUE(file != nullptr);
fprintf(file, "YUV4MPEG2 W6 H4 F60:1 C420 dummyParam\n");
fprintf(file, "FRAME\n");
const int i420_size = width * height * 3 / 2;
// First frame.
for (int i = 0; i < i420_size; ++i)
fputc(static_cast<char>(i), file);
fprintf(file, "FRAME\n");
// Second frame.
for (int i = 0; i < i420_size; ++i)
fputc(static_cast<char>(i + i420_size), file);
fclose(file);
// Open the newly created file.
video = webrtc::test::OpenY4mFile(filename);
ASSERT_TRUE(video);
}
// Write and read Y4M file.
void WriteVideoY4m() {
const std::string filename =
webrtc::test::OutputPath() + "test_video_file2.y4m";
webrtc::test::WriteVideoToFile(video, filename, fps);
written_video = webrtc::test::OpenY4mFile(filename);
ASSERT_TRUE(written_video);
}
// Write and read YUV file.
void WriteVideoYuv() {
const std::string filename =
webrtc::test::OutputPath() + "test_video_file2.yuv";
webrtc::test::WriteVideoToFile(video, filename, fps);
written_video = webrtc::test::OpenYuvFile(filename, width, height);
ASSERT_TRUE(written_video);
}
const int width = 6;
const int height = 4;
const int fps = 60;
rtc::scoped_refptr<webrtc::test::Video> video;
rtc::scoped_refptr<webrtc::test::Video> written_video;
};
TEST_F(VideoFileWriterTest, TestParsingFileHeaderY4m) {
WriteVideoY4m();
EXPECT_EQ(video->width(), written_video->width());
EXPECT_EQ(video->height(), written_video->height());
}
TEST_F(VideoFileWriterTest, TestParsingFileHeaderYuv) {
WriteVideoYuv();
EXPECT_EQ(video->width(), written_video->width());
EXPECT_EQ(video->height(), written_video->height());
}
TEST_F(VideoFileWriterTest, TestParsingNumberOfFramesY4m) {
WriteVideoY4m();
EXPECT_EQ(video->number_of_frames(), written_video->number_of_frames());
}
TEST_F(VideoFileWriterTest, TestParsingNumberOfFramesYuv) {
WriteVideoYuv();
EXPECT_EQ(video->number_of_frames(), written_video->number_of_frames());
}
TEST_F(VideoFileWriterTest, TestPixelContentY4m) {
WriteVideoY4m();
int cnt = 0;
for (const rtc::scoped_refptr<I420BufferInterface> frame : *written_video) {
for (int i = 0; i < width * height; ++i, ++cnt)
EXPECT_EQ(cnt, frame->DataY()[i]);
for (int i = 0; i < width / 2 * height / 2; ++i, ++cnt)
EXPECT_EQ(cnt, frame->DataU()[i]);
for (int i = 0; i < width / 2 * height / 2; ++i, ++cnt)
EXPECT_EQ(cnt, frame->DataV()[i]);
}
}
TEST_F(VideoFileWriterTest, TestPixelContentYuv) {
WriteVideoYuv();
int cnt = 0;
for (const rtc::scoped_refptr<I420BufferInterface> frame : *written_video) {
for (int i = 0; i < width * height; ++i, ++cnt)
EXPECT_EQ(cnt, frame->DataY()[i]);
for (int i = 0; i < width / 2 * height / 2; ++i, ++cnt)
EXPECT_EQ(cnt, frame->DataU()[i]);
for (int i = 0; i < width / 2 * height / 2; ++i, ++cnt)
EXPECT_EQ(cnt, frame->DataV()[i]);
}
}
} // namespace test
} // namespace webrtc