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}
diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn
index ae80585..437dca4 100644
--- a/rtc_tools/BUILD.gn
+++ b/rtc_tools/BUILD.gn
@@ -73,6 +73,20 @@
]
}
+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 @@
deps = [
":command_line_parser",
":video_file_reader",
+ ":video_file_writer",
":video_quality_analysis",
"../rtc_base:stringutils",
"../test:perf_test",
@@ -343,6 +358,7 @@
"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 @@
":frame_editing_lib",
":reference_less_video_analysis_lib",
":video_file_reader",
+ ":video_file_writer",
":video_quality_analysis",
"../common_video:common_video",
"../rtc_base",
diff --git a/rtc_tools/compare_videos.py b/rtc_tools/compare_videos.py
index 40a2aab..d9cb670 100755
--- a/rtc_tools/compare_videos.py
+++ b/rtc_tools/compare_videos.py
@@ -36,6 +36,8 @@
'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 @@
"""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 @@
]
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()
diff --git a/rtc_tools/frame_analyzer/frame_analyzer.cc b/rtc_tools/frame_analyzer/frame_analyzer.cc
index aab2758..49d381f 100644
--- a/rtc_tools/frame_analyzer/frame_analyzer.cc
+++ b/rtc_tools/frame_analyzer/frame_analyzer.cc
@@ -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 @@
" 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 @@
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 @@
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;
}
diff --git a/rtc_tools/frame_analyzer/video_temporal_aligner.cc b/rtc_tools/frame_analyzer/video_temporal_aligner.cc
index f106852..2ebffbc 100644
--- a/rtc_tools/frame_analyzer/video_temporal_aligner.cc
+++ b/rtc_tools/frame_analyzer/video_temporal_aligner.cc
@@ -221,8 +221,14 @@
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
diff --git a/rtc_tools/frame_analyzer/video_temporal_aligner.h b/rtc_tools/frame_analyzer/video_temporal_aligner.h
index 6f2015a..6024618 100644
--- a/rtc_tools/frame_analyzer/video_temporal_aligner.h
+++ b/rtc_tools/frame_analyzer/video_temporal_aligner.h
@@ -47,6 +47,11 @@
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
diff --git a/rtc_tools/video_file_writer.cc b/rtc_tools/video_file_writer.cc
new file mode 100644
index 0000000..72f3a69
--- /dev/null
+++ b/rtc_tools/video_file_writer.cc
@@ -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
diff --git a/rtc_tools/video_file_writer.h b/rtc_tools/video_file_writer.h
new file mode 100644
index 0000000..bd4524b
--- /dev/null
+++ b/rtc_tools/video_file_writer.h
@@ -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_
diff --git a/rtc_tools/video_file_writer_unittest.cc b/rtc_tools/video_file_writer_unittest.cc
new file mode 100644
index 0000000..11b1c37
--- /dev/null
+++ b/rtc_tools/video_file_writer_unittest.cc
@@ -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