webrtc_m130/test/testsupport/yuv_frame_reader.cc
Erik Språng ebe5acb27a VideoCodecTextFixture and YuvFrameReader improvements.
Adds ability to specify desired frame size separate from actual clip
resolution, as well as clip and desired fps.
This allows e.g. reading an HD clip but running benchmarks in VGA, and
to specify e.g. 60fps for the clip but 30for encoding where frame
dropping kicks in so that motion is actually correct rather than just
plaing the clip slowly.

Bug: webrtc:12229
Change-Id: I4ad4fcc335611a449dc2723ffafbec6731e89f55
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/195324
Commit-Queue: Erik Språng <sprang@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32839}
2020-12-15 23:18:06 +00:00

179 lines
5.8 KiB
C++

/*
* Copyright (c) 2017 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/scoped_refptr.h"
#include "api/video/i420_buffer.h"
#include "test/frame_utils.h"
#include "test/testsupport/file_utils.h"
#include "test/testsupport/frame_reader.h"
namespace webrtc {
namespace test {
size_t FrameSizeBytes(int width, int height) {
int half_width = (width + 1) / 2;
size_t size_y = static_cast<size_t>(width) * height;
size_t size_uv = static_cast<size_t>(half_width) * ((height + 1) / 2);
return size_y + 2 * size_uv;
}
YuvFrameReaderImpl::DropperUtil::DropperUtil(int source_fps, int target_fps)
: frame_size_buckets_(
std::max(1.0, static_cast<double>(source_fps) / target_fps)),
bucket_level_(0.0) {}
YuvFrameReaderImpl::DropperUtil::DropDecision
YuvFrameReaderImpl::DropperUtil::UpdateLevel() {
DropDecision decision;
if (bucket_level_ <= 0.0) {
decision = DropDecision::kKeepFrame;
bucket_level_ += frame_size_buckets_;
} else {
decision = DropDecision::kDropframe;
}
bucket_level_ -= 1.0;
return decision;
}
YuvFrameReaderImpl::YuvFrameReaderImpl(std::string input_filename,
int width,
int height)
: YuvFrameReaderImpl(input_filename,
width,
height,
width,
height,
RepeatMode::kSingle,
30,
30) {}
YuvFrameReaderImpl::YuvFrameReaderImpl(std::string input_filename,
int input_width,
int input_height,
int desired_width,
int desired_height,
RepeatMode repeat_mode,
absl::optional<int> clip_fps,
int target_fps)
: input_filename_(input_filename),
frame_length_in_bytes_(input_width * input_height +
2 * ((input_width + 1) / 2) *
((input_height + 1) / 2)),
input_width_(input_width),
input_height_(input_height),
desired_width_(desired_width),
desired_height_(desired_height),
frame_size_bytes_(FrameSizeBytes(input_width, input_height)),
repeat_mode_(repeat_mode),
number_of_frames_(-1),
current_frame_index_(-1),
dropper_(clip_fps.has_value() ? new DropperUtil(*clip_fps, target_fps)
: nullptr),
input_file_(nullptr) {}
YuvFrameReaderImpl::~YuvFrameReaderImpl() {
Close();
}
bool YuvFrameReaderImpl::Init() {
if (input_width_ <= 0 || input_height_ <= 0) {
fprintf(stderr, "Frame width and height must be >0, was %d x %d\n",
input_width_, input_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;
}
// Calculate total number of frames.
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;
}
number_of_frames_ =
static_cast<int>(source_file_size / frame_length_in_bytes_);
current_frame_index_ = 0;
return true;
}
rtc::scoped_refptr<I420Buffer> YuvFrameReaderImpl::ReadFrame() {
if (input_file_ == nullptr) {
fprintf(stderr,
"YuvFrameReaderImpl is not initialized (input file is NULL)\n");
return nullptr;
}
rtc::scoped_refptr<I420Buffer> buffer;
do {
if (current_frame_index_ >= number_of_frames_) {
switch (repeat_mode_) {
case RepeatMode::kSingle:
return nullptr;
case RepeatMode::kRepeat:
fseek(input_file_, 0, SEEK_SET);
current_frame_index_ = 0;
break;
case RepeatMode::kPingPong:
if (current_frame_index_ == number_of_frames_ * 2) {
fseek(input_file_, 0, SEEK_SET);
current_frame_index_ = 0;
} else {
int reverse_frame_index = current_frame_index_ - number_of_frames_;
int seek_frame_pos = (number_of_frames_ - reverse_frame_index - 1);
fseek(input_file_, seek_frame_pos * frame_size_bytes_, SEEK_SET);
}
break;
}
}
++current_frame_index_;
buffer = ReadI420Buffer(input_width_, input_height_, input_file_);
if (!buffer && ferror(input_file_)) {
fprintf(stderr, "Error reading from input file: %s\n",
input_filename_.c_str());
}
} while (dropper_ &&
dropper_->UpdateLevel() == DropperUtil::DropDecision::kDropframe);
if (input_width_ == desired_width_ && input_height_ == desired_height_) {
return buffer;
}
rtc::scoped_refptr<I420Buffer> rescaled_buffer(
I420Buffer::Create(desired_width_, desired_height_));
rescaled_buffer->ScaleFrom(*buffer.get());
return rescaled_buffer;
}
void YuvFrameReaderImpl::Close() {
if (input_file_ != nullptr) {
fclose(input_file_);
input_file_ = nullptr;
}
}
size_t YuvFrameReaderImpl::FrameLength() {
return frame_length_in_bytes_;
}
int YuvFrameReaderImpl::NumberOfFrames() {
return number_of_frames_;
}
} // namespace test
} // namespace webrtc