Add Y4mFileReader
Encapsulate logic for reading .y4m video files in a single class. We
currently have spread out logic for opening .y4m files with partial
parsing. This CL consolidates this logic into a single class with a well
defined interface.
Change-Id: Id61673b3c95a0053b30e95b4cf382e1c6b05fc30
Bug: webrtc:9642
Reviewed-on: https://webrtc-review.googlesource.com/94772
Reviewed-by: Patrik Höglund <phoglund@webrtc.org>
Reviewed-by: Paulina Hensman <phensman@webrtc.org>
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24398}
diff --git a/resources/video_quality_analysis_frame.txt.sha1 b/resources/video_quality_analysis_frame.txt.sha1
deleted file mode 100644
index e4a7c73..0000000
--- a/resources/video_quality_analysis_frame.txt.sha1
+++ /dev/null
@@ -1 +0,0 @@
-4d1ac894f1743af8059e8d8ae2465f6eaa1790b0
\ No newline at end of file
diff --git a/rtc_tools/BUILD.gn b/rtc_tools/BUILD.gn
index 99f1ae6..f6b03da 100644
--- a/rtc_tools/BUILD.gn
+++ b/rtc_tools/BUILD.gn
@@ -17,6 +17,7 @@
":command_line_parser",
":frame_analyzer",
":video_quality_analysis",
+ ":y4m_file_reader",
]
if (!build_with_chromium) {
deps += [
@@ -59,12 +60,28 @@
]
}
+rtc_static_library("y4m_file_reader") {
+ sources = [
+ "y4m_file_reader.cc",
+ "y4m_file_reader.h",
+ ]
+ deps = [
+ "../api/video:video_frame",
+ "../api/video:video_frame_i420",
+ "../rtc_base:rtc_base_approved",
+ "../rtc_base:sequenced_task_checker",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+}
+
rtc_static_library("video_quality_analysis") {
sources = [
"frame_analyzer/video_quality_analysis.cc",
"frame_analyzer/video_quality_analysis.h",
]
deps = [
+ ":y4m_file_reader",
+ "../api/video:video_frame_i420",
"../common_video",
"../test:perf_test",
"//third_party/libyuv",
@@ -80,6 +97,7 @@
deps = [
":command_line_parser",
":video_quality_analysis",
+ ":y4m_file_reader",
"../test:perf_test",
"//build/win:default_exe_manifest",
]
@@ -96,6 +114,7 @@
deps = [
":command_line_parser",
":video_quality_analysis",
+ ":y4m_file_reader",
"//build/win:default_exe_manifest",
]
}
@@ -108,6 +127,7 @@
deps = [
":video_quality_analysis",
+ ":y4m_file_reader",
]
}
@@ -288,7 +308,6 @@
tools_unittests_resources = [
"../resources/foreman_cif.yuv",
"../resources/reference_less_video_test_file.y4m",
- "../resources/video_quality_analysis_frame.txt",
]
if (is_ios) {
@@ -310,6 +329,7 @@
"frame_editing/frame_editing_unittest.cc",
"sanitizers_unittest.cc",
"simple_command_line_parser_unittest.cc",
+ "y4m_file_reader_unittest.cc",
]
if (!build_with_chromium && is_clang) {
@@ -322,6 +342,7 @@
":frame_editing_lib",
":reference_less_video_analysis_lib",
":video_quality_analysis",
+ ":y4m_file_reader",
"../common_video:common_video",
"../rtc_base",
"../rtc_base:checks",
diff --git a/rtc_tools/frame_analyzer/frame_analyzer.cc b/rtc_tools/frame_analyzer/frame_analyzer.cc
index b3d9b94..f5a9c2b 100644
--- a/rtc_tools/frame_analyzer/frame_analyzer.cc
+++ b/rtc_tools/frame_analyzer/frame_analyzer.cc
@@ -17,6 +17,7 @@
#include "rtc_tools/frame_analyzer/video_quality_analysis.h"
#include "rtc_tools/simple_command_line_parser.h"
+#include "rtc_tools/y4m_file_reader.h"
#include "test/testsupport/perf_test.h"
/*
@@ -101,11 +102,19 @@
webrtc::test::ResultsContainer results;
- webrtc::test::RunAnalysis(parser.GetFlag("reference_file").c_str(),
- parser.GetFlag("test_file").c_str(),
- parser.GetFlag("stats_file_ref").c_str(),
- parser.GetFlag("stats_file_test").c_str(), width,
- height, &results);
+ rtc::scoped_refptr<webrtc::test::Y4mFile> reference_video =
+ webrtc::test::Y4mFile::Open(parser.GetFlag("reference_file"));
+ rtc::scoped_refptr<webrtc::test::Y4mFile> test_video =
+ webrtc::test::Y4mFile::Open(parser.GetFlag("test_file"));
+
+ if (!reference_video || !test_video) {
+ fprintf(stderr, "Error opening video files\n");
+ return 0;
+ }
+
+ webrtc::test::RunAnalysis(
+ reference_video, test_video, parser.GetFlag("stats_file_ref").c_str(),
+ parser.GetFlag("stats_file_test").c_str(), width, height, &results);
webrtc::test::GetMaxRepeatedAndSkippedFrames(
parser.GetFlag("stats_file_ref"), parser.GetFlag("stats_file_test"),
&results);
diff --git a/rtc_tools/frame_analyzer/reference_less_video_analysis_lib.cc b/rtc_tools/frame_analyzer/reference_less_video_analysis_lib.cc
index 9a7535b..96e0ef4 100644
--- a/rtc_tools/frame_analyzer/reference_less_video_analysis_lib.cc
+++ b/rtc_tools/frame_analyzer/reference_less_video_analysis_lib.cc
@@ -25,36 +25,6 @@
#define strtok_r strtok_s
#endif
-void get_height_width_fps(int* height,
- int* width,
- int* fps,
- const std::string& video_file) {
- // File header looks like :
- // YUV4MPEG2 W1280 H720 F25:1 Ip A0:0 C420mpeg2 XYSCSS=420MPEG2.
- char frame_header[STATS_LINE_LENGTH];
- FILE* input_file = fopen(video_file.c_str(), "rb");
-
- size_t bytes_read = fread(frame_header, 1, STATS_LINE_LENGTH - 1, input_file);
-
- frame_header[bytes_read] = '\0';
- std::string file_header_stats[5];
- int no_of_stats = 0;
- char* save_ptr;
- char* token = strtok_r(frame_header, " ", &save_ptr);
-
- while (token != NULL) {
- file_header_stats[no_of_stats++] = token;
- token = strtok_r(NULL, " ", &save_ptr);
- }
-
- *width = std::stoi(file_header_stats[1].erase(0, 1));
- *height = std::stoi(file_header_stats[2].erase(0, 1));
- *fps = std::stoi(file_header_stats[3].erase(0, 1));
-
- printf("Height: %d Width: %d fps:%d \n", *height, *width, *fps);
- fclose(input_file);
-}
-
bool frozen_frame(std::vector<double> psnr_per_frame,
std::vector<double> ssim_per_frame,
size_t frame) {
@@ -135,60 +105,29 @@
printf("\n");
}
-void compute_metrics(const std::string& video_file_name,
+void compute_metrics(const rtc::scoped_refptr<webrtc::test::Y4mFile>& video,
std::vector<double>* psnr_per_frame,
std::vector<double>* ssim_per_frame) {
- int height = 0, width = 0, fps = 0;
- get_height_width_fps(&height, &width, &fps, video_file_name);
-
- int no_of_frames = 0;
- int size = webrtc::test::GetI420FrameSize(width, height);
-
- // Allocate buffers for test and reference frames.
- uint8_t* current_frame = new uint8_t[size];
- uint8_t* next_frame = new uint8_t[size];
-
- while (true) {
- if (!(webrtc::test::ExtractFrameFromY4mFile(video_file_name.c_str(), width,
- height, no_of_frames,
- current_frame))) {
- break;
- }
-
- if (!(webrtc::test::ExtractFrameFromY4mFile(video_file_name.c_str(), width,
- height, no_of_frames + 1,
- next_frame))) {
- break;
- }
-
- double result_psnr = webrtc::test::CalculateMetrics(
- webrtc::test::kPSNR, current_frame, next_frame, width, height);
- double result_ssim = webrtc::test::CalculateMetrics(
- webrtc::test::kSSIM, current_frame, next_frame, width, height);
+ for (size_t i = 0; i < video->number_of_frames() - 1; ++i) {
+ const rtc::scoped_refptr<webrtc::I420BufferInterface> current_frame =
+ video->GetFrame(i);
+ const rtc::scoped_refptr<webrtc::I420BufferInterface> next_frame =
+ video->GetFrame(i + 1);
+ double result_psnr = webrtc::test::Psnr(current_frame, next_frame);
+ double result_ssim = webrtc::test::Ssim(current_frame, next_frame);
psnr_per_frame->push_back(result_psnr);
ssim_per_frame->push_back(result_ssim);
- no_of_frames++;
}
- // Cleanup.
- delete[] current_frame;
- delete[] next_frame;
-}
-
-bool check_file_extension(const std::string& video_file_name) {
- if (video_file_name.substr(video_file_name.length() - 3, 3) != "y4m") {
- printf("Only y4m video file format is supported. Given: %s\n",
- video_file_name.c_str());
- return false;
- }
- return true;
}
int run_analysis(const std::string& video_file) {
std::vector<double> psnr_per_frame;
std::vector<double> ssim_per_frame;
- if (check_file_extension(video_file)) {
- compute_metrics(video_file, &psnr_per_frame, &ssim_per_frame);
+ rtc::scoped_refptr<webrtc::test::Y4mFile> video =
+ webrtc::test::Y4mFile::Open(video_file);
+ if (video) {
+ compute_metrics(video, &psnr_per_frame, &ssim_per_frame);
} else {
return -1;
}
diff --git a/rtc_tools/frame_analyzer/reference_less_video_analysis_lib.h b/rtc_tools/frame_analyzer/reference_less_video_analysis_lib.h
index a1de03b..6da9f4e 100644
--- a/rtc_tools/frame_analyzer/reference_less_video_analysis_lib.h
+++ b/rtc_tools/frame_analyzer/reference_less_video_analysis_lib.h
@@ -14,12 +14,7 @@
#include <string>
#include <vector>
-// Parse the file header to extract height, width and fps
-// for a given video file.
-void get_height_width_fps(int* height,
- int* width,
- int* fps,
- const std::string& video_file);
+#include "rtc_tools/y4m_file_reader.h"
// Returns true if the frame is frozen based on psnr and ssim freezing
// threshold values.
@@ -39,13 +34,10 @@
// Compute the metrics like freezing score based on PSNR and SSIM values for a
// given video file.
-void compute_metrics(const std::string& video_file_name,
+void compute_metrics(const rtc::scoped_refptr<webrtc::test::Y4mFile>& video,
std::vector<double>* psnr_per_frame,
std::vector<double>* ssim_per_frame);
-// Checks the file extension and return true if it is y4m.
-bool check_file_extension(const std::string& video_file_name);
-
// Compute freezing score metrics and prints the metrics
// for a list of video files.
int run_analysis(const std::string& video_file);
diff --git a/rtc_tools/frame_analyzer/reference_less_video_analysis_unittest.cc b/rtc_tools/frame_analyzer/reference_less_video_analysis_unittest.cc
index 4e20532..2c3cf3a 100644
--- a/rtc_tools/frame_analyzer/reference_less_video_analysis_unittest.cc
+++ b/rtc_tools/frame_analyzer/reference_less_video_analysis_unittest.cc
@@ -20,16 +20,18 @@
class ReferenceLessVideoAnalysisTest : public ::testing::Test {
public:
void SetUp() override {
- video_file =
- webrtc::test::ResourcePath("reference_less_video_test_file", "y4m");
+ video = webrtc::test::Y4mFile::Open(
+ webrtc::test::ResourcePath("reference_less_video_test_file", "y4m"));
+ ASSERT_TRUE(video);
}
- std::string video_file;
+
+ rtc::scoped_refptr<webrtc::test::Y4mFile> video;
std::vector<double> psnr_per_frame;
std::vector<double> ssim_per_frame;
};
TEST_F(ReferenceLessVideoAnalysisTest, MatchComputedMetrics) {
- compute_metrics(video_file, &psnr_per_frame, &ssim_per_frame);
+ compute_metrics(video, &psnr_per_frame, &ssim_per_frame);
EXPECT_EQ(74, (int)psnr_per_frame.size());
ASSERT_NEAR(27.2f, psnr_per_frame[1], 0.1f);
@@ -39,26 +41,11 @@
ASSERT_NEAR(0.9f, ssim_per_frame[5], 0.1f);
}
-TEST_F(ReferenceLessVideoAnalysisTest, MatchHeightWidthFps) {
- int height = 0, width = 0, fps = 0;
- get_height_width_fps(&height, &width, &fps, video_file.c_str());
- EXPECT_EQ(height, 720);
- EXPECT_EQ(width, 1280);
- EXPECT_EQ(fps, 25);
-}
-
TEST_F(ReferenceLessVideoAnalysisTest, MatchIdenticalFrameClusters) {
- compute_metrics(video_file, &psnr_per_frame, &ssim_per_frame);
+ compute_metrics(video, &psnr_per_frame, &ssim_per_frame);
std::vector<int> identical_frame_clusters =
find_frame_clusters(psnr_per_frame, ssim_per_frame);
EXPECT_EQ(5, (int)identical_frame_clusters.size());
EXPECT_EQ(1, identical_frame_clusters[0]);
EXPECT_EQ(1, identical_frame_clusters[4]);
}
-
-TEST_F(ReferenceLessVideoAnalysisTest, CheckFileExtension) {
- EXPECT_TRUE(check_file_extension(video_file));
- std::string txt_file =
- webrtc::test::ResourcePath("video_quality_analysis_frame", "txt");
- EXPECT_FALSE(check_file_extension(txt_file));
-}
diff --git a/rtc_tools/frame_analyzer/video_quality_analysis.cc b/rtc_tools/frame_analyzer/video_quality_analysis.cc
index 60a4a01..dda55b8 100644
--- a/rtc_tools/frame_analyzer/video_quality_analysis.cc
+++ b/rtc_tools/frame_analyzer/video_quality_analysis.cc
@@ -19,11 +19,10 @@
#include <utility>
#include "test/testsupport/perf_test.h"
+#include "third_party/libyuv/include/libyuv/compare.h"
+#include "third_party/libyuv/include/libyuv/convert.h"
#define STATS_LINE_LENGTH 32
-#define Y4M_FILE_HEADER_MAX_SIZE 200
-#define Y4M_FRAME_DELIMITER "FRAME"
-#define Y4M_FRAME_HEADER_SIZE 6
namespace webrtc {
namespace test {
@@ -92,160 +91,47 @@
return true;
}
-bool ExtractFrameFromYuvFile(const char* i420_file_name,
- int width,
- int height,
- int frame_number,
- uint8_t* result_frame) {
- int frame_size = GetI420FrameSize(width, height);
- int offset = frame_number * frame_size; // Calculate offset for the frame.
- bool errors = false;
-
- FILE* input_file = fopen(i420_file_name, "rb");
- if (input_file == NULL) {
- fprintf(stderr, "Couldn't open input file for reading: %s\n",
- i420_file_name);
- return false;
- }
-
- // Change stream pointer to new offset.
- fseek(input_file, offset, SEEK_SET);
-
- size_t bytes_read = fread(result_frame, 1, frame_size, input_file);
- if (bytes_read != static_cast<size_t>(frame_size) && ferror(input_file)) {
- fprintf(stdout, "Error while reading frame no %d from file %s\n",
- frame_number, i420_file_name);
- errors = true;
- }
- fclose(input_file);
- return !errors;
+template <typename FrameMetricFunction>
+static double CalculateMetric(
+ const FrameMetricFunction& frame_metric_function,
+ const rtc::scoped_refptr<I420BufferInterface>& ref_buffer,
+ const rtc::scoped_refptr<I420BufferInterface>& test_buffer) {
+ RTC_CHECK_EQ(ref_buffer->width(), test_buffer->width());
+ RTC_CHECK_EQ(ref_buffer->height(), test_buffer->height());
+ return frame_metric_function(
+ ref_buffer->DataY(), ref_buffer->StrideY(), ref_buffer->DataU(),
+ ref_buffer->StrideU(), ref_buffer->DataV(), ref_buffer->StrideV(),
+ test_buffer->DataY(), test_buffer->StrideY(), test_buffer->DataU(),
+ test_buffer->StrideU(), test_buffer->DataV(), test_buffer->StrideV(),
+ test_buffer->width(), test_buffer->height());
}
-bool ExtractFrameFromY4mFile(const char* y4m_file_name,
- int width,
- int height,
- int frame_number,
- uint8_t* result_frame) {
- int frame_size = GetI420FrameSize(width, height);
- int inital_offset = frame_number * (frame_size + Y4M_FRAME_HEADER_SIZE);
- int frame_offset = 0;
-
- FILE* input_file = fopen(y4m_file_name, "rb");
- if (input_file == NULL) {
- fprintf(stderr, "Couldn't open input file for reading: %s\n",
- y4m_file_name);
- return false;
- }
-
- // YUV4MPEG2, a.k.a. Y4M File format has a file header and a frame header. The
- // file header has the aspect: "YUV4MPEG2 C420 W640 H360 Ip F30:1 A1:1".
- char frame_header[Y4M_FILE_HEADER_MAX_SIZE];
- size_t bytes_read =
- fread(frame_header, 1, Y4M_FILE_HEADER_MAX_SIZE - 1, input_file);
- if (bytes_read != static_cast<size_t>(frame_size) && ferror(input_file)) {
- fprintf(stdout, "Error while reading frame from file %s\n", y4m_file_name);
- fclose(input_file);
- return false;
- }
- frame_header[bytes_read] = '\0';
- std::string header_contents(frame_header);
- std::size_t found = header_contents.find(Y4M_FRAME_DELIMITER);
- if (found == std::string::npos) {
- fprintf(stdout, "Corrupted Y4M header, could not find \"FRAME\" in %s\n",
- header_contents.c_str());
- fclose(input_file);
- return false;
- }
- frame_offset = static_cast<int>(found);
-
- // Change stream pointer to new offset, skipping the frame header as well.
- fseek(input_file, inital_offset + frame_offset + Y4M_FRAME_HEADER_SIZE,
- SEEK_SET);
-
- bytes_read = fread(result_frame, 1, frame_size, input_file);
- if (feof(input_file)) {
- fclose(input_file);
- return false;
- }
- if (bytes_read != static_cast<size_t>(frame_size) && ferror(input_file)) {
- fprintf(stdout, "Error while reading frame no %d from file %s\n",
- frame_number, y4m_file_name);
- fclose(input_file);
- return false;
- }
-
- fclose(input_file);
- return true;
+double Psnr(const rtc::scoped_refptr<I420BufferInterface>& ref_buffer,
+ const rtc::scoped_refptr<I420BufferInterface>& test_buffer) {
+ // LibYuv sets the max psnr value to 128, we restrict it to 48.
+ // In case of 0 mse in one frame, 128 can skew the results significantly.
+ return std::min(48.0,
+ CalculateMetric(&libyuv::I420Psnr, ref_buffer, test_buffer));
}
-double CalculateMetrics(VideoAnalysisMetricsType video_metrics_type,
- const uint8_t* ref_frame,
- const uint8_t* test_frame,
- int width,
- int height) {
- if (!ref_frame || !test_frame)
- return -1;
- else if (height < 0 || width < 0)
- return -1;
- int half_width = (width + 1) >> 1;
- int half_height = (height + 1) >> 1;
- const uint8_t* src_y_a = ref_frame;
- const uint8_t* src_u_a = src_y_a + width * height;
- const uint8_t* src_v_a = src_u_a + half_width * half_height;
- const uint8_t* src_y_b = test_frame;
- const uint8_t* src_u_b = src_y_b + width * height;
- const uint8_t* src_v_b = src_u_b + half_width * half_height;
-
- int stride_y = width;
- int stride_uv = half_width;
-
- double result = 0.0;
-
- switch (video_metrics_type) {
- case kPSNR:
- // In the following: stride is determined by width.
- result = libyuv::I420Psnr(src_y_a, width, src_u_a, half_width, src_v_a,
- half_width, src_y_b, width, src_u_b, half_width,
- src_v_b, half_width, width, height);
- // LibYuv sets the max psnr value to 128, we restrict it to 48.
- // In case of 0 mse in one frame, 128 can skew the results significantly.
- result = (result > 48.0) ? 48.0 : result;
- break;
- case kSSIM:
- result = libyuv::I420Ssim(src_y_a, stride_y, src_u_a, stride_uv, src_v_a,
- stride_uv, src_y_b, stride_y, src_u_b,
- stride_uv, src_v_b, stride_uv, width, height);
- break;
- default:
- assert(false);
- }
-
- return result;
+double Ssim(const rtc::scoped_refptr<I420BufferInterface>& ref_buffer,
+ const rtc::scoped_refptr<I420BufferInterface>& test_buffer) {
+ return CalculateMetric(&libyuv::I420Ssim, ref_buffer, test_buffer);
}
-void RunAnalysis(const char* reference_file_name,
- const char* test_file_name,
+void RunAnalysis(const rtc::scoped_refptr<webrtc::test::Video>& reference_video,
+ const rtc::scoped_refptr<webrtc::test::Video>& test_video,
const char* stats_file_reference_name,
const char* stats_file_test_name,
int width,
int height,
ResultsContainer* results) {
- // Check if the reference_file_name ends with "y4m".
- bool y4m_mode = false;
- if (std::string(reference_file_name).find("y4m") != std::string::npos) {
- y4m_mode = true;
- }
-
- int size = GetI420FrameSize(width, height);
FILE* stats_file_ref = fopen(stats_file_reference_name, "r");
FILE* stats_file_test = fopen(stats_file_test_name, "r");
// String buffer for the lines in the stats file.
char line[STATS_LINE_LENGTH];
- // Allocate buffers for test and reference frames.
- uint8_t* test_frame = new uint8_t[size];
- uint8_t* reference_frame = new uint8_t[size];
int previous_frame_number = -1;
// Maps barcode id to the frame id for the reference video.
@@ -282,21 +168,14 @@
assert(extracted_test_frame != -1);
assert(decoded_frame_number != -1);
- ExtractFrameFromYuvFile(test_file_name, width, height, extracted_test_frame,
- test_frame);
- if (y4m_mode) {
- ExtractFrameFromY4mFile(reference_file_name, width, height,
- extracted_ref_frame, reference_frame);
- } else {
- ExtractFrameFromYuvFile(reference_file_name, width, height,
- extracted_ref_frame, reference_frame);
- }
+ const rtc::scoped_refptr<webrtc::I420BufferInterface> test_frame =
+ test_video->GetFrame(extracted_test_frame);
+ const rtc::scoped_refptr<webrtc::I420BufferInterface> reference_frame =
+ reference_video->GetFrame(extracted_ref_frame);
// Calculate the PSNR and SSIM.
- double result_psnr =
- CalculateMetrics(kPSNR, reference_frame, test_frame, width, height);
- double result_ssim =
- CalculateMetrics(kSSIM, reference_frame, test_frame, width, height);
+ double result_psnr = Psnr(reference_frame, test_frame);
+ double result_ssim = Ssim(reference_frame, test_frame);
previous_frame_number = decoded_frame_number;
@@ -312,8 +191,6 @@
// Cleanup.
fclose(stats_file_ref);
fclose(stats_file_test);
- delete[] test_frame;
- delete[] reference_frame;
}
std::vector<std::pair<int, int> > CalculateFrameClusters(
diff --git a/rtc_tools/frame_analyzer/video_quality_analysis.h b/rtc_tools/frame_analyzer/video_quality_analysis.h
index dca719d..0ebd0c7 100644
--- a/rtc_tools/frame_analyzer/video_quality_analysis.h
+++ b/rtc_tools/frame_analyzer/video_quality_analysis.h
@@ -15,8 +15,8 @@
#include <utility>
#include <vector>
-#include "third_party/libyuv/include/libyuv/compare.h"
-#include "third_party/libyuv/include/libyuv/convert.h"
+#include "api/video/i420_buffer.h"
+#include "rtc_tools/y4m_file_reader.h"
namespace webrtc {
namespace test {
@@ -44,8 +44,6 @@
int decode_errors_test;
};
-enum VideoAnalysisMetricsType { kPSNR, kSSIM };
-
// A function to run the PSNR and SSIM analysis on the test file. The test file
// comprises the frames that were captured during the quality measurement test.
// There may be missing or duplicate frames. Also the frames start at a random
@@ -61,23 +59,23 @@
// problem with the decoding there would be 'Barcode error' instead of yyyy.
// The stat files are used to compare the right frames with each other and
// to calculate statistics.
-void RunAnalysis(const char* reference_file_name,
- const char* test_file_name,
+void RunAnalysis(const rtc::scoped_refptr<webrtc::test::Video>& reference_video,
+ const rtc::scoped_refptr<webrtc::test::Video>& test_video,
const char* stats_file_reference_name,
const char* stats_file_test_name,
int width,
int height,
ResultsContainer* results);
-// Compute PSNR or SSIM for an I420 frame (all planes). When we are calculating
-// PSNR values, the max return value (in the case where the test and reference
-// frames are exactly the same) will be 48. In the case of SSIM the max return
-// value will be 1.
-double CalculateMetrics(VideoAnalysisMetricsType video_metrics_type,
- const uint8_t* ref_frame,
- const uint8_t* test_frame,
- int width,
- int height);
+// Compute PSNR for an I420 buffer (all planes). The max return value (in the
+// case where the test and reference frames are exactly the same) will be 48.
+double Psnr(const rtc::scoped_refptr<I420BufferInterface>& ref_buffer,
+ const rtc::scoped_refptr<I420BufferInterface>& test_buffer);
+
+// Compute SSIM for an I420 buffer (all planes). The max return value (in the
+// case where the test and reference frames are exactly the same) will be 1.
+double Ssim(const rtc::scoped_refptr<I420BufferInterface>& ref_buffer,
+ const rtc::scoped_refptr<I420BufferInterface>& test_buffer);
// Prints the result from the analysis in Chromium performance
// numbers compatible format to stdout. If the results object contains no frames
@@ -129,21 +127,6 @@
// frame_0023 0284, we will get 284.
int ExtractDecodedFrameNumber(std::string line);
-// Extracts an I420 frame at position frame_number from the raw YUV file.
-bool ExtractFrameFromYuvFile(const char* i420_file_name,
- int width,
- int height,
- int frame_number,
- uint8_t* result_frame);
-
-// Extracts an I420 frame at position frame_number from the Y4M file. The first
-// frame has corresponded |frame_number| 0.
-bool ExtractFrameFromY4mFile(const char* i420_file_name,
- int width,
- int height,
- int frame_number,
- uint8_t* result_frame);
-
} // namespace test
} // namespace webrtc
diff --git a/rtc_tools/frame_analyzer/video_quality_analysis_unittest.cc b/rtc_tools/frame_analyzer/video_quality_analysis_unittest.cc
index d1edb30..d9565b2 100644
--- a/rtc_tools/frame_analyzer/video_quality_analysis_unittest.cc
+++ b/rtc_tools/frame_analyzer/video_quality_analysis_unittest.cc
@@ -41,31 +41,6 @@
std::string stats_filename_;
};
-TEST_F(VideoQualityAnalysisTest, MatchExtractedY4mFrame) {
- std::string video_file =
- webrtc::test::ResourcePath("reference_less_video_test_file", "y4m");
-
- std::string extracted_frame_from_video_file =
- webrtc::test::ResourcePath("video_quality_analysis_frame", "txt");
-
- int frame_height = 720, frame_width = 1280;
- int frame_number = 2;
- int size = GetI420FrameSize(frame_width, frame_height);
- uint8_t* result_frame = new uint8_t[size];
- uint8_t* expected_frame = new uint8_t[size];
-
- FILE* input_file = fopen(extracted_frame_from_video_file.c_str(), "rb");
- fread(expected_frame, 1, size, input_file);
-
- ExtractFrameFromY4mFile(video_file.c_str(), frame_width, frame_height,
- frame_number, result_frame);
-
- EXPECT_EQ(*expected_frame, *result_frame);
- fclose(input_file);
- delete[] result_frame;
- delete[] expected_frame;
-}
-
TEST_F(VideoQualityAnalysisTest, PrintAnalysisResultsEmpty) {
ResultsContainer result;
PrintAnalysisResults(logfile_, "Empty", &result);
diff --git a/rtc_tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc b/rtc_tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc
index dc63aca..b995f54 100644
--- a/rtc_tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc
+++ b/rtc_tools/psnr_ssim_analyzer/psnr_ssim_analyzer.cc
@@ -8,70 +8,42 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
-#include <map>
#include <string>
-#include <vector>
#include "rtc_tools/frame_analyzer/video_quality_analysis.h"
#include "rtc_tools/simple_command_line_parser.h"
+#include "rtc_tools/y4m_file_reader.h"
-#define MAX_NUM_FRAMES_PER_FILE INT_MAX
-
-void CompareFiles(const char* reference_file_name,
- const char* test_file_name,
- const char* results_file_name,
- int width,
- int height) {
- // Check if the reference_file_name ends with "y4m".
- bool y4m_mode = false;
- if (std::string(reference_file_name).find("y4m") != std::string::npos) {
- y4m_mode = true;
- }
-
+void CompareFiles(
+ const rtc::scoped_refptr<webrtc::test::Video>& reference_video,
+ const rtc::scoped_refptr<webrtc::test::Video>& test_video,
+ const char* results_file_name) {
FILE* results_file = fopen(results_file_name, "w");
- int size = webrtc::test::GetI420FrameSize(width, height);
-
- // Allocate buffers for test and reference frames.
- uint8_t* test_frame = new uint8_t[size];
- uint8_t* ref_frame = new uint8_t[size];
-
- bool read_result = true;
- for (int frame_counter = 0; frame_counter < MAX_NUM_FRAMES_PER_FILE;
- ++frame_counter) {
- read_result &=
- (y4m_mode)
- ? webrtc::test::ExtractFrameFromY4mFile(
- reference_file_name, width, height, frame_counter, ref_frame)
- : webrtc::test::ExtractFrameFromYuvFile(
- reference_file_name, width, height, frame_counter, ref_frame);
- read_result &= webrtc::test::ExtractFrameFromYuvFile(
- test_file_name, width, height, frame_counter, test_frame);
-
- if (!read_result)
- break;
+ const size_t num_frames = std::min(reference_video->number_of_frames(),
+ test_video->number_of_frames());
+ for (size_t i = 0; i < num_frames; ++i) {
+ const rtc::scoped_refptr<webrtc::I420BufferInterface> ref_buffer =
+ reference_video->GetFrame(i);
+ const rtc::scoped_refptr<webrtc::I420BufferInterface> test_buffer =
+ test_video->GetFrame(i);
// Calculate the PSNR and SSIM.
- double result_psnr = webrtc::test::CalculateMetrics(
- webrtc::test::kPSNR, ref_frame, test_frame, width, height);
- double result_ssim = webrtc::test::CalculateMetrics(
- webrtc::test::kSSIM, ref_frame, test_frame, width, height);
- fprintf(results_file, "Frame: %d, PSNR: %f, SSIM: %f\n", frame_counter,
- result_psnr, result_ssim);
+ double result_psnr = webrtc::test::Psnr(ref_buffer, test_buffer);
+ double result_ssim = webrtc::test::Ssim(ref_buffer, test_buffer);
+ fprintf(results_file, "Frame: %zu, PSNR: %f, SSIM: %f\n", i, result_psnr,
+ result_ssim);
}
- delete[] test_frame;
- delete[] ref_frame;
fclose(results_file);
}
/*
* A tool running PSNR and SSIM analysis on two videos - a reference video and a
- * test video. The two videos should be I420 YUV videos.
+ * test video. The two videos should be I420 Y4M videos.
* The tool just runs PSNR and SSIM on the corresponding frames in the test and
* the reference videos until either the first or the second video runs out of
* frames. The result is written in a results text file in the format:
@@ -82,8 +54,7 @@
*
* Usage:
* psnr_ssim_analyzer --reference_file=<name_of_file> --test_file=<name_of_file>
- * --results_file=<name_of_file> --width=<width_of_frames>
- * --height=<height_of_frames>
+ * --results_file=<name_of_file>
*/
int main(int argc, char* argv[]) {
std::string program_name = argv[0];
@@ -93,12 +64,8 @@
"Example usage:\n" +
program_name +
" --reference_file=ref.yuv "
- "--test_file=test.yuv --results_file=results.txt --width=320 "
- "--height=240\n"
+ "--test_file=test.yuv --results_file=results.txt\n"
"Command line flags:\n"
- " - width(int): The width of the reference and test files. Default: -1\n"
- " - height(int): The height of the reference and test files. "
- " Default: -1\n"
" - reference_file(string): The reference YUV file to compare against."
" Default: ref.yuv\n"
" - test_file(string): The test YUV file to run the analysis for."
@@ -112,8 +79,6 @@
parser.Init(argc, argv);
parser.SetUsageMessage(usage);
- parser.SetFlag("width", "-1");
- parser.SetFlag("height", "-1");
parser.SetFlag("results_file", "results.txt");
parser.SetFlag("reference_file", "ref.yuv");
parser.SetFlag("test_file", "test.yuv");
@@ -127,16 +92,26 @@
}
parser.PrintEnteredFlags();
- int width = strtol((parser.GetFlag("width")).c_str(), NULL, 10);
- int height = strtol((parser.GetFlag("height")).c_str(), NULL, 10);
+ rtc::scoped_refptr<webrtc::test::Y4mFile> reference_video =
+ webrtc::test::Y4mFile::Open(parser.GetFlag("reference_file"));
+ rtc::scoped_refptr<webrtc::test::Y4mFile> test_video =
+ webrtc::test::Y4mFile::Open(parser.GetFlag("test_file"));
- if (width <= 0 || height <= 0) {
- fprintf(stderr, "Error: width or height cannot be <= 0!\n");
- return -1;
+ if (!reference_video || !test_video) {
+ fprintf(stderr, "Error opening video files\n");
+ return 0;
+ }
+ if (reference_video->width() != test_video->width() ||
+ reference_video->height() != test_video->height()) {
+ fprintf(stderr,
+ "Reference and test video files do not have same size: %dx%d "
+ "versus %dx%d\n",
+ reference_video->width(), reference_video->height(),
+ test_video->width(), test_video->height());
+ return 0;
}
- CompareFiles(parser.GetFlag("reference_file").c_str(),
- parser.GetFlag("test_file").c_str(),
- parser.GetFlag("results_file").c_str(), width, height);
+ CompareFiles(reference_video, test_video,
+ parser.GetFlag("results_file").c_str());
return 0;
}
diff --git a/rtc_tools/y4m_file_reader.cc b/rtc_tools/y4m_file_reader.cc
new file mode 100644
index 0000000..e2bc399
--- /dev/null
+++ b/rtc_tools/y4m_file_reader.cc
@@ -0,0 +1,234 @@
+/*
+ * 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/y4m_file_reader.h"
+
+#include <algorithm>
+#include <cmath>
+#include <cstdio>
+#include <string>
+#include <utility>
+
+#include "absl/types/optional.h"
+#include "api/video/i420_buffer.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/refcountedobject.h"
+#include "rtc_base/sequenced_task_checker.h"
+#include "rtc_base/string_to_number.h"
+#include "rtc_base/stringencode.h"
+#include "rtc_base/stringutils.h"
+
+namespace webrtc {
+namespace test {
+
+Video::Iterator::Iterator(const rtc::scoped_refptr<const Video>& video,
+ size_t index)
+ : video_(video), index_(index) {}
+
+Video::Iterator::Iterator(const Video::Iterator& other) = default;
+Video::Iterator::Iterator(Video::Iterator&& other) = default;
+Video::Iterator& Video::Iterator::operator=(Video::Iterator&&) = default;
+Video::Iterator& Video::Iterator::operator=(const Video::Iterator&) = default;
+Video::Iterator::~Iterator() = default;
+
+rtc::scoped_refptr<I420BufferInterface> Video::Iterator::operator*() const {
+ return video_->GetFrame(index_);
+}
+bool Video::Iterator::operator==(const Video::Iterator& other) const {
+ return index_ == other.index_;
+}
+bool Video::Iterator::operator!=(const Video::Iterator& other) const {
+ return !(*this == other);
+}
+
+Video::Iterator Video::Iterator::operator++(int) {
+ const Iterator copy = *this;
+ ++*this;
+ return copy;
+}
+
+Video::Iterator& Video::Iterator::operator++() {
+ ++index_;
+ return *this;
+}
+
+Video::Iterator Video::begin() const {
+ return Iterator(this, 0);
+}
+
+Video::Iterator Video::end() const {
+ return Iterator(this, number_of_frames());
+}
+
+rtc::scoped_refptr<Y4mFile> Y4mFile::Open(const std::string& file_name) {
+ FILE* file = fopen(file_name.c_str(), "rb");
+ if (file == nullptr) {
+ RTC_LOG(LS_ERROR) << "Could not open input file for reading: " << file_name;
+ return nullptr;
+ }
+
+ int parse_file_header_result = -1;
+ fscanf(file, "YUV4MPEG2 %n", &parse_file_header_result);
+ if (parse_file_header_result == -1) {
+ RTC_LOG(LS_ERROR) << "File " << file_name
+ << " does not start with YUV4MPEG2 header";
+ return nullptr;
+ }
+
+ std::string header_line;
+ while (true) {
+ const int c = fgetc(file);
+ if (c == EOF) {
+ RTC_LOG(LS_ERROR) << "Could not read header line";
+ return nullptr;
+ }
+ if (c == '\n')
+ break;
+ header_line.push_back(static_cast<char>(c));
+ }
+
+ absl::optional<int> width;
+ absl::optional<int> height;
+ absl::optional<float> fps;
+
+ std::vector<std::string> fields;
+ rtc::tokenize(header_line, ' ', &fields);
+ for (const std::string& field : fields) {
+ const char prefix = field.front();
+ const std::string suffix = field.substr(1);
+ switch (prefix) {
+ case 'W':
+ width = rtc::StringToNumber<int>(suffix);
+ break;
+ case 'H':
+ height = rtc::StringToNumber<int>(suffix);
+ break;
+ case 'C':
+ if (suffix != "420" && suffix != "420mpeg2") {
+ RTC_LOG(LS_ERROR)
+ << "Does not support any other color space than I420 or "
+ "420mpeg2, but was: "
+ << suffix;
+ return nullptr;
+ }
+ break;
+ case 'F': {
+ std::vector<std::string> fraction;
+ rtc::tokenize(suffix, ':', &fraction);
+ if (fraction.size() == 2) {
+ const absl::optional<int> numerator =
+ rtc::StringToNumber<int>(fraction[0]);
+ const absl::optional<int> denominator =
+ rtc::StringToNumber<int>(fraction[1]);
+ if (numerator && denominator && *denominator != 0)
+ fps = *numerator / static_cast<float>(*denominator);
+ break;
+ }
+ }
+ }
+ }
+ if (!width || !height) {
+ RTC_LOG(LS_ERROR) << "Could not find width and height in file header";
+ return nullptr;
+ }
+ if (!fps) {
+ RTC_LOG(LS_ERROR) << "Could not find fps in file header";
+ return nullptr;
+ }
+ RTC_LOG(LS_INFO) << "Video has resolution: " << *width << "x" << *height
+ << " " << *fps << " fps";
+ if (*width % 2 != 0 || *height % 2 != 0) {
+ RTC_LOG(LS_ERROR)
+ << "Only supports even width/height so that chroma size is a "
+ "whole number.";
+ return nullptr;
+ }
+
+ const int i420_frame_size = 3 * *width * *height / 2;
+ std::vector<fpos_t> frame_positions;
+ while (true) {
+ int parse_frame_header_result = -1;
+ fscanf(file, "FRAME\n%n", &parse_frame_header_result);
+ if (parse_frame_header_result == -1) {
+ if (!feof(file)) {
+ RTC_LOG(LS_ERROR) << "Did not find FRAME header, ignoring rest of file";
+ }
+ break;
+ }
+ fpos_t pos;
+ fgetpos(file, &pos);
+ frame_positions.push_back(pos);
+ // Skip over YUV pixel data.
+ fseek(file, i420_frame_size, SEEK_CUR);
+ }
+ if (frame_positions.empty()) {
+ RTC_LOG(LS_ERROR) << "Could not find any frames in the file";
+ return nullptr;
+ }
+ RTC_LOG(LS_INFO) << "Video has " << frame_positions.size() << " frames";
+
+ return new rtc::RefCountedObject<Y4mFile>(*width, *height, *fps,
+ frame_positions, file);
+}
+
+size_t Y4mFile::number_of_frames() const {
+ return frame_positions_.size();
+}
+
+rtc::scoped_refptr<I420BufferInterface> Y4mFile::GetFrame(
+ size_t frame_index) const {
+ RTC_DCHECK_CALLED_SEQUENTIALLY(&thread_checker_);
+ RTC_CHECK_LT(frame_index, frame_positions_.size());
+
+ fsetpos(file_, &frame_positions_[frame_index]);
+ rtc::scoped_refptr<I420Buffer> buffer = I420Buffer::Create(width_, height_);
+ fread(reinterpret_cast<char*>(buffer->MutableDataY()), /* size= */ 1,
+ width_ * height_, file_);
+ fread(reinterpret_cast<char*>(buffer->MutableDataU()), /* size= */ 1,
+ buffer->ChromaWidth() * buffer->ChromaHeight(), file_);
+ fread(reinterpret_cast<char*>(buffer->MutableDataV()), /* size= */ 1,
+ buffer->ChromaWidth() * buffer->ChromaHeight(), file_);
+
+ if (ferror(file_) != 0) {
+ RTC_LOG(LS_ERROR) << "Could not read YUV data for frame " << frame_index;
+ return nullptr;
+ }
+ return buffer;
+}
+
+int Y4mFile::width() const {
+ return width_;
+}
+
+int Y4mFile::height() const {
+ return height_;
+}
+
+float Y4mFile::fps() const {
+ return fps_;
+}
+
+Y4mFile::Y4mFile(int width,
+ int height,
+ float fps,
+ const std::vector<fpos_t>& frame_positions,
+ FILE* file)
+ : width_(width),
+ height_(height),
+ fps_(fps),
+ frame_positions_(frame_positions),
+ file_(file) {}
+
+Y4mFile::~Y4mFile() {
+ fclose(file_);
+}
+
+} // namespace test
+} // namespace webrtc
diff --git a/rtc_tools/y4m_file_reader.h b/rtc_tools/y4m_file_reader.h
new file mode 100644
index 0000000..b11beef
--- /dev/null
+++ b/rtc_tools/y4m_file_reader.h
@@ -0,0 +1,99 @@
+/*
+ * 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_Y4M_FILE_READER_H_
+#define RTC_TOOLS_Y4M_FILE_READER_H_
+
+#include <cstdio>
+#include <iterator>
+#include <string>
+#include <vector>
+
+#include "api/video/video_frame.h"
+#include "rtc_base/refcount.h"
+#include "rtc_base/sequenced_task_checker.h"
+
+namespace webrtc {
+namespace test {
+
+// Iterable class representing a sequence of I420 buffers. This class is not
+// thread safe because it is expected to be backed by a file.
+class Video : public rtc::RefCountInterface {
+ public:
+ class Iterator {
+ public:
+ typedef int value_type;
+ typedef std::ptrdiff_t difference_type;
+ typedef int* pointer;
+ typedef int& reference;
+ typedef std::input_iterator_tag iterator_category;
+
+ Iterator(const rtc::scoped_refptr<const Video>& video, size_t index);
+ Iterator(const Iterator& other);
+ Iterator(Iterator&& other);
+ Iterator& operator=(Iterator&&);
+ Iterator& operator=(const Iterator&);
+ ~Iterator();
+
+ rtc::scoped_refptr<I420BufferInterface> operator*() const;
+ bool operator==(const Iterator& other) const;
+ bool operator!=(const Iterator& other) const;
+
+ Iterator operator++(int);
+ Iterator& operator++();
+
+ private:
+ rtc::scoped_refptr<const Video> video_;
+ size_t index_;
+ };
+
+ Iterator begin() const;
+ Iterator end() const;
+
+ virtual size_t number_of_frames() const = 0;
+ virtual rtc::scoped_refptr<I420BufferInterface> GetFrame(
+ size_t index) const = 0;
+};
+
+class Y4mFile : public Video {
+ public:
+ // This function opens the file and reads it as an .y4m file. It returns null
+ // on failure. The file will be closed when the returned object is destroyed.
+ static rtc::scoped_refptr<Y4mFile> Open(const std::string& file_name);
+
+ size_t number_of_frames() const override;
+
+ rtc::scoped_refptr<I420BufferInterface> GetFrame(
+ size_t frame_index) const override;
+
+ int width() const;
+ int height() const;
+ float fps() const;
+
+ protected:
+ Y4mFile(int width,
+ int height,
+ float fps,
+ const std::vector<fpos_t>& frame_positions,
+ FILE* file);
+ ~Y4mFile() override;
+
+ private:
+ const int width_;
+ const int height_;
+ const float fps_;
+ const std::vector<fpos_t> frame_positions_;
+ const rtc::SequencedTaskChecker thread_checker_;
+ FILE* const file_;
+};
+
+} // namespace test
+} // namespace webrtc
+
+#endif // RTC_TOOLS_Y4M_FILE_READER_H_
diff --git a/rtc_tools/y4m_file_reader_unittest.cc b/rtc_tools/y4m_file_reader_unittest.cc
new file mode 100644
index 0000000..3c6c92f
--- /dev/null
+++ b/rtc_tools/y4m_file_reader_unittest.cc
@@ -0,0 +1,71 @@
+/*
+ * 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/y4m_file_reader.h"
+#include "test/gtest.h"
+#include "test/testsupport/fileutils.h"
+
+namespace webrtc {
+namespace test {
+
+class Y4mFileReaderTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ const std::string filename =
+ TempFilename(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 F57:10 C420 dummyParam\n");
+ fprintf(file, "FRAME\n");
+
+ const int width = 6;
+ const int height = 4;
+ const int i40_size = width * height * 3 / 2;
+ for (int i = 0; i < i40_size; ++i)
+ fputc(static_cast<char>(i), file);
+ fprintf(file, "FRAME\n");
+ for (int i = 0; i < i40_size; ++i)
+ fputc(static_cast<char>(i + i40_size), file);
+ fclose(file);
+
+ // Open the newly created file.
+ video = webrtc::test::Y4mFile::Open(filename);
+ ASSERT_TRUE(video);
+ }
+
+ rtc::scoped_refptr<webrtc::test::Y4mFile> video;
+};
+
+TEST_F(Y4mFileReaderTest, TestParsingFileHeader) {
+ EXPECT_EQ(6, video->width());
+ EXPECT_EQ(4, video->height());
+ EXPECT_EQ(5.7f, video->fps());
+}
+
+TEST_F(Y4mFileReaderTest, TestParsingNumberOfFrames) {
+ EXPECT_EQ(2u, video->number_of_frames());
+}
+
+TEST_F(Y4mFileReaderTest, TestPixelContent) {
+ int cnt = 0;
+ for (const rtc::scoped_refptr<I420BufferInterface> frame : *video) {
+ for (int i = 0; i < 6 * 4; ++i, ++cnt)
+ EXPECT_EQ(cnt, frame->DataY()[i]);
+ for (int i = 0; i < 3 * 2; ++i, ++cnt)
+ EXPECT_EQ(cnt, frame->DataU()[i]);
+ for (int i = 0; i < 3 * 2; ++i, ++cnt)
+ EXPECT_EQ(cnt, frame->DataV()[i]);
+ }
+}
+
+} // namespace test
+} // namespace webrtc