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