| /* | |
| * 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 <charconv> | |
| #include <cstdio> | |
| #include <cstring> | |
| #include <memory> | |
| #include <string> | |
| #include <vector> | |
| #include "absl/strings/string_view.h" | |
| #include "api/video/resolution.h" | |
| #include "common_video/libyuv/include/webrtc_libyuv.h" | |
| #include "rtc_base/checks.h" | |
| #include "rtc_base/string_encode.h" | |
| #include "test/testsupport/file_utils.h" | |
| #include "test/testsupport/frame_reader.h" | |
| namespace webrtc { | |
| namespace test { | |
| namespace { | |
| constexpr int kFrameHeaderSize = 6; // "FRAME\n" | |
| } // namespace | |
| void ParseY4mHeader(std::string filepath, | |
| Resolution* resolution, | |
| int* header_size) { | |
| FILE* file = fopen(filepath.c_str(), "r"); | |
| RTC_CHECK(file != nullptr) << "Cannot open " << filepath; | |
| // Length of Y4M header is technically unlimited due to the comment tag 'X'. | |
| char h[1024]; | |
| RTC_CHECK(fgets(h, sizeof(h), file) != nullptr) | |
| << "File " << filepath << " is too small"; | |
| fclose(file); | |
| std::vector<absl::string_view> header = split(h, ' '); | |
| RTC_CHECK(!header.empty() && header[0] == "YUV4MPEG2") | |
| << filepath << " is not a valid Y4M file"; | |
| for (size_t i = 1; i < header.size(); ++i) { | |
| RTC_CHECK(!header[i].empty()); | |
| switch (header[i][0]) { | |
| case 'W': { | |
| auto n = header[i].substr(1); | |
| std::from_chars(n.data(), n.data() + n.size(), resolution->width); | |
| continue; | |
| } | |
| case 'H': { | |
| auto n = header[i].substr(1); | |
| std::from_chars(n.data(), n.data() + n.size(), resolution->height); | |
| continue; | |
| } | |
| default: { | |
| continue; | |
| } | |
| } | |
| } | |
| RTC_CHECK_GT(resolution->width, 0) << "Width must be positive"; | |
| RTC_CHECK_GT(resolution->height, 0) << "Height must be positive"; | |
| *header_size = strcspn(h, "\n") + 1; | |
| RTC_CHECK(static_cast<unsigned>(*header_size) < sizeof(h)) | |
| << filepath << " has unexpectedly large header"; | |
| } | |
| Y4mFrameReaderImpl::Y4mFrameReaderImpl(std::string filepath, | |
| RepeatMode repeat_mode) | |
| : YuvFrameReaderImpl(filepath, Resolution(), repeat_mode) {} | |
| void Y4mFrameReaderImpl::Init() { | |
| file_ = fopen(filepath_.c_str(), "rb"); | |
| RTC_CHECK(file_ != nullptr) << "Cannot open " << filepath_; | |
| ParseY4mHeader(filepath_, &resolution_, &header_size_bytes_); | |
| frame_size_bytes_ = | |
| CalcBufferSize(VideoType::kI420, resolution_.width, resolution_.height); | |
| frame_size_bytes_ += kFrameHeaderSize; | |
| size_t file_size_bytes = GetFileSize(filepath_); | |
| RTC_CHECK_GT(file_size_bytes, 0u) << "File " << filepath_ << " is empty"; | |
| RTC_CHECK_GT(file_size_bytes, header_size_bytes_) | |
| << "File " << filepath_ << " is too small"; | |
| num_frames_ = static_cast<int>((file_size_bytes - header_size_bytes_) / | |
| frame_size_bytes_); | |
| RTC_CHECK_GT(num_frames_, 0u) << "File " << filepath_ << " is too small"; | |
| header_size_bytes_ += kFrameHeaderSize; | |
| } | |
| std::unique_ptr<FrameReader> CreateY4mFrameReader(std::string filepath) { | |
| return CreateY4mFrameReader(filepath, | |
| YuvFrameReaderImpl::RepeatMode::kSingle); | |
| } | |
| std::unique_ptr<FrameReader> CreateY4mFrameReader( | |
| std::string filepath, | |
| YuvFrameReaderImpl::RepeatMode repeat_mode) { | |
| Y4mFrameReaderImpl* frame_reader = | |
| new Y4mFrameReaderImpl(filepath, repeat_mode); | |
| frame_reader->Init(); | |
| return std::unique_ptr<FrameReader>(frame_reader); | |
| } | |
| } // namespace test | |
| } // namespace webrtc |