| /* | |
| * 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 "common_video/libyuv/include/webrtc_libyuv.h" | |
| #include "rtc_base/logging.h" | |
| #include "test/frame_utils.h" | |
| #include "test/testsupport/file_utils.h" | |
| #include "test/testsupport/frame_reader.h" | |
| namespace webrtc { | |
| namespace test { | |
| namespace { | |
| using RepeatMode = YuvFrameReaderImpl::RepeatMode; | |
| int WrapFrameNum(int frame_num, int num_frames, RepeatMode mode) { | |
| RTC_CHECK_GE(frame_num, 0) << "frame_num cannot be negative"; | |
| RTC_CHECK_GT(num_frames, 0) << "num_frames must be greater than 0"; | |
| if (mode == RepeatMode::kSingle) { | |
| return frame_num; | |
| } | |
| if (mode == RepeatMode::kRepeat) { | |
| return frame_num % num_frames; | |
| } | |
| RTC_CHECK_EQ(RepeatMode::kPingPong, mode); | |
| int cycle_len = std::max(1, 2 * (num_frames - 1)); | |
| int wrapped_num = frame_num % cycle_len; | |
| if (wrapped_num >= num_frames) { | |
| return cycle_len - wrapped_num; | |
| } | |
| return wrapped_num; | |
| } | |
| scoped_refptr<I420Buffer> Scale(scoped_refptr<I420Buffer> buffer, | |
| Resolution resolution) { | |
| if (buffer->width() == resolution.width && | |
| buffer->height() == resolution.height) { | |
| return buffer; | |
| } | |
| scoped_refptr<I420Buffer> scaled( | |
| I420Buffer::Create(resolution.width, resolution.height)); | |
| scaled->ScaleFrom(*buffer.get()); | |
| return scaled; | |
| } | |
| } // namespace | |
| int YuvFrameReaderImpl::RateScaler::Skip(Ratio framerate_scale) { | |
| ticks_ = ticks_.value_or(framerate_scale.num); | |
| int skip = 0; | |
| while (ticks_ <= 0) { | |
| *ticks_ += framerate_scale.num; | |
| ++skip; | |
| } | |
| *ticks_ -= framerate_scale.den; | |
| return skip; | |
| } | |
| YuvFrameReaderImpl::YuvFrameReaderImpl(std::string filepath, | |
| Resolution resolution, | |
| RepeatMode repeat_mode) | |
| : filepath_(filepath), | |
| resolution_(resolution), | |
| repeat_mode_(repeat_mode), | |
| num_frames_(0), | |
| frame_num_(0), | |
| frame_size_bytes_(0), | |
| header_size_bytes_(0), | |
| file_(nullptr) {} | |
| YuvFrameReaderImpl::~YuvFrameReaderImpl() { | |
| if (file_ != nullptr) { | |
| fclose(file_); | |
| file_ = nullptr; | |
| } | |
| } | |
| void YuvFrameReaderImpl::Init() { | |
| RTC_CHECK_GT(resolution_.width, 0) << "Width must be positive"; | |
| RTC_CHECK_GT(resolution_.height, 0) << "Height must be positive"; | |
| frame_size_bytes_ = | |
| CalcBufferSize(VideoType::kI420, resolution_.width, resolution_.height); | |
| file_ = fopen(filepath_.c_str(), "rb"); | |
| RTC_CHECK(file_ != NULL) << "Cannot open " << filepath_; | |
| size_t file_size_bytes = GetFileSize(filepath_); | |
| RTC_CHECK_GT(file_size_bytes, 0u) << "File " << filepath_ << " is empty"; | |
| num_frames_ = static_cast<int>(file_size_bytes / frame_size_bytes_); | |
| RTC_CHECK_GT(num_frames_, 0u) << "File " << filepath_ << " is too small"; | |
| } | |
| scoped_refptr<I420Buffer> YuvFrameReaderImpl::PullFrame() { | |
| return PullFrame(/*frame_num=*/nullptr); | |
| } | |
| scoped_refptr<I420Buffer> YuvFrameReaderImpl::PullFrame(int* frame_num) { | |
| return PullFrame(frame_num, resolution_, /*framerate_scale=*/kNoScale); | |
| } | |
| scoped_refptr<I420Buffer> YuvFrameReaderImpl::PullFrame(int* frame_num, | |
| Resolution resolution, | |
| Ratio framerate_scale) { | |
| frame_num_ += framerate_scaler_.Skip(framerate_scale); | |
| auto buffer = ReadFrame(frame_num_, resolution); | |
| if (frame_num != nullptr) { | |
| *frame_num = frame_num_; | |
| } | |
| return buffer; | |
| } | |
| scoped_refptr<I420Buffer> YuvFrameReaderImpl::ReadFrame(int frame_num) { | |
| return ReadFrame(frame_num, resolution_); | |
| } | |
| scoped_refptr<I420Buffer> YuvFrameReaderImpl::ReadFrame(int frame_num, | |
| Resolution resolution) { | |
| int wrapped_num = WrapFrameNum(frame_num, num_frames_, repeat_mode_); | |
| if (wrapped_num >= num_frames_) { | |
| RTC_CHECK_EQ(RepeatMode::kSingle, repeat_mode_); | |
| return nullptr; | |
| } | |
| fseek(file_, header_size_bytes_ + wrapped_num * frame_size_bytes_, SEEK_SET); | |
| auto buffer = ReadI420Buffer(resolution_.width, resolution_.height, file_); | |
| RTC_CHECK(buffer != nullptr); | |
| return Scale(buffer, resolution); | |
| } | |
| std::unique_ptr<FrameReader> CreateYuvFrameReader(std::string filepath, | |
| Resolution resolution) { | |
| return CreateYuvFrameReader(filepath, resolution, | |
| YuvFrameReaderImpl::RepeatMode::kSingle); | |
| } | |
| std::unique_ptr<FrameReader> CreateYuvFrameReader( | |
| std::string filepath, | |
| Resolution resolution, | |
| YuvFrameReaderImpl::RepeatMode repeat_mode) { | |
| YuvFrameReaderImpl* frame_reader = | |
| new YuvFrameReaderImpl(filepath, resolution, repeat_mode); | |
| frame_reader->Init(); | |
| return std::unique_ptr<FrameReader>(frame_reader); | |
| } | |
| } // namespace test | |
| } // namespace webrtc |