Introduce Y4mFrameReader.

Bug: webrtc:10138
Change-Id: I213a4309a8a4b1a7afd296bf45566c9b3f9a215c
Reviewed-on: https://webrtc-review.googlesource.com/c/117301
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26243}
This commit is contained in:
Artem Titov 2019-01-14 14:21:59 +01:00 committed by Commit Bot
parent a8c7326524
commit 645df9e3b5
5 changed files with 201 additions and 5 deletions

View File

@ -254,6 +254,7 @@ if (rtc_include_tests) {
"testsupport/frame_reader.h",
"testsupport/frame_writer.h",
"testsupport/mock/mock_frame_reader.h",
"testsupport/y4m_frame_reader.cc",
"testsupport/y4m_frame_writer.cc",
"testsupport/yuv_frame_reader.cc",
"testsupport/yuv_frame_writer.cc",
@ -352,6 +353,7 @@ if (rtc_include_tests) {
"//testing/gmock",
"//testing/gtest",
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/strings",
]
sources = [
"direct_transport_unittest.cc",
@ -363,6 +365,7 @@ if (rtc_include_tests) {
"testsupport/always_passing_unittest.cc",
"testsupport/perf_test_unittest.cc",
"testsupport/test_artifacts_unittest.cc",
"testsupport/y4m_frame_reader_unittest.cc",
"testsupport/y4m_frame_writer_unittest.cc",
"testsupport/yuv_frame_reader_unittest.cc",
"testsupport/yuv_frame_writer_unittest.cc",

View File

@ -59,8 +59,9 @@ class YuvFrameReaderImpl : public FrameReader {
size_t FrameLength() override;
int NumberOfFrames() override;
private:
protected:
const std::string input_filename_;
// It is not const, so subclasses will be able to add frame header size.
size_t frame_length_in_bytes_;
const int width_;
const int height_;
@ -68,6 +69,22 @@ class YuvFrameReaderImpl : public FrameReader {
FILE* input_file_;
};
class Y4mFrameReaderImpl : public YuvFrameReaderImpl {
public:
// Creates a file handler. The input file is assumed to exist and be readable.
// Parameters:
// input_filename The file to read from.
// width, height Size of each frame to read.
Y4mFrameReaderImpl(std::string input_filename, int width, int height);
~Y4mFrameReaderImpl() override;
bool Init() override;
rtc::scoped_refptr<I420Buffer> ReadFrame() override;
private:
// Buffer that is used to read file and frame headers.
uint8_t* buffer_;
};
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,85 @@
/*
* 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 <stdio.h>
#include <string>
#include "api/video/i420_buffer.h"
#include "rtc_base/scoped_ref_ptr.h"
#include "test/testsupport/file_utils.h"
#include "test/testsupport/frame_reader.h"
namespace webrtc {
namespace test {
namespace {
// Size of header: "YUV4MPEG2 W50 H20 F30:1 C420\n"
const size_t kFileHeaderSize = 29;
// Size of header: "FRAME\n"
const size_t kFrameHeaderSize = 6;
} // namespace
Y4mFrameReaderImpl::Y4mFrameReaderImpl(std::string input_filename,
int width,
int height)
: YuvFrameReaderImpl(input_filename, width, height) {
frame_length_in_bytes_ += kFrameHeaderSize;
buffer_ = new uint8_t[kFileHeaderSize];
}
Y4mFrameReaderImpl::~Y4mFrameReaderImpl() {
delete[] buffer_;
}
bool Y4mFrameReaderImpl::Init() {
if (width_ <= 0 || height_ <= 0) {
fprintf(stderr, "Frame width and height must be >0, was %d x %d\n", width_,
height_);
return false;
}
input_file_ = fopen(input_filename_.c_str(), "rb");
if (input_file_ == nullptr) {
fprintf(stderr, "Couldn't open input file for reading: %s\n",
input_filename_.c_str());
return false;
}
size_t source_file_size = GetFileSize(input_filename_);
if (source_file_size <= 0u) {
fprintf(stderr, "Found empty file: %s\n", input_filename_.c_str());
return false;
}
if (fread(buffer_, 1, kFileHeaderSize, input_file_) < kFileHeaderSize) {
fprintf(stderr, "Failed to read file header from input file: %s\n",
input_filename_.c_str());
return false;
}
// Calculate total number of frames.
number_of_frames_ = static_cast<int>((source_file_size - kFileHeaderSize) /
frame_length_in_bytes_);
return true;
}
rtc::scoped_refptr<I420Buffer> Y4mFrameReaderImpl::ReadFrame() {
if (input_file_ == nullptr) {
fprintf(stderr,
"Y4mFrameReaderImpl is not initialized (input file is NULL)\n");
return nullptr;
}
if (fread(buffer_, 1, kFrameHeaderSize, input_file_) < kFrameHeaderSize &&
ferror(input_file_)) {
fprintf(stderr, "Failed to read frame header from input file: %s\n",
input_filename_.c_str());
return nullptr;
}
return YuvFrameReaderImpl::ReadFrame();
}
} // namespace test
} // namespace webrtc

View File

@ -0,0 +1,93 @@
/*
* 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 <stdint.h>
#include <stdio.h>
#include <memory>
#include <string>
#include "absl/strings/string_view.h"
#include "api/video/i420_buffer.h"
#include "api/video/video_frame_buffer.h"
#include "rtc_base/scoped_ref_ptr.h"
#include "test/gtest.h"
#include "test/testsupport/file_utils.h"
#include "test/testsupport/frame_reader.h"
namespace webrtc {
namespace test {
namespace {
const absl::string_view kFileHeader = "YUV4MPEG2 W50 H20 F30:1 C420\n";
const absl::string_view kFrameHeader = "FRAME\n";
const absl::string_view kInputVideoContents = "abcdef";
const size_t kFrameWidth = 2;
const size_t kFrameHeight = 2;
const size_t kFrameLength = 3 * kFrameWidth * kFrameHeight / 2; // I420.
} // namespace
class Y4mFrameReaderTest : public testing::Test {
protected:
Y4mFrameReaderTest() = default;
~Y4mFrameReaderTest() override = default;
void SetUp() override {
temp_filename_ = webrtc::test::TempFilename(webrtc::test::OutputPath(),
"y4m_frame_reader_unittest");
FILE* dummy = fopen(temp_filename_.c_str(), "wb");
fprintf(dummy, "%s",
(std::string(kFileHeader) + std::string(kFrameHeader) +
std::string(kInputVideoContents))
.c_str());
fclose(dummy);
frame_reader_.reset(
new Y4mFrameReaderImpl(temp_filename_, kFrameWidth, kFrameHeight));
ASSERT_TRUE(frame_reader_->Init());
}
void TearDown() override { remove(temp_filename_.c_str()); }
std::unique_ptr<FrameReader> frame_reader_;
std::string temp_filename_;
};
TEST_F(Y4mFrameReaderTest, InitSuccess) {}
TEST_F(Y4mFrameReaderTest, FrameLength) {
EXPECT_EQ(kFrameHeader.size() + kFrameLength, frame_reader_->FrameLength());
}
TEST_F(Y4mFrameReaderTest, NumberOfFrames) {
EXPECT_EQ(1, frame_reader_->NumberOfFrames());
}
TEST_F(Y4mFrameReaderTest, ReadFrame) {
rtc::scoped_refptr<I420BufferInterface> buffer = frame_reader_->ReadFrame();
ASSERT_TRUE(buffer);
// Expect I420 packed as YUV.
EXPECT_EQ(kInputVideoContents[0], buffer->DataY()[0]);
EXPECT_EQ(kInputVideoContents[1], buffer->DataY()[1]);
EXPECT_EQ(kInputVideoContents[2], buffer->DataY()[2]);
EXPECT_EQ(kInputVideoContents[3], buffer->DataY()[3]);
EXPECT_EQ(kInputVideoContents[4], buffer->DataU()[0]);
EXPECT_EQ(kInputVideoContents[5], buffer->DataV()[0]);
EXPECT_FALSE(frame_reader_->ReadFrame()); // End of file.
}
TEST_F(Y4mFrameReaderTest, ReadFrameUninitialized) {
Y4mFrameReaderImpl file_reader(temp_filename_, kFrameWidth, kFrameHeight);
EXPECT_FALSE(file_reader.ReadFrame());
}
} // namespace test
} // namespace webrtc

View File

@ -24,7 +24,8 @@ YuvFrameReaderImpl::YuvFrameReaderImpl(std::string input_filename,
int width,
int height)
: input_filename_(input_filename),
frame_length_in_bytes_(0),
frame_length_in_bytes_(width * height +
2 * ((width + 1) / 2) * ((height + 1) / 2)),
width_(width),
height_(height),
number_of_frames_(-1),
@ -40,9 +41,6 @@ bool YuvFrameReaderImpl::Init() {
height_);
return false;
}
frame_length_in_bytes_ =
width_ * height_ + 2 * ((width_ + 1) / 2) * ((height_ + 1) / 2);
input_file_ = fopen(input_filename_.c_str(), "rb");
if (input_file_ == nullptr) {
fprintf(stderr, "Couldn't open input file for reading: %s\n",