summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/rtc_tools/video_file_reader.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /third_party/libwebrtc/rtc_tools/video_file_reader.cc
parentInitial commit. (diff)
downloadthunderbird-upstream.tar.xz
thunderbird-upstream.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/rtc_tools/video_file_reader.cc')
-rw-r--r--third_party/libwebrtc/rtc_tools/video_file_reader.cc288
1 files changed, 288 insertions, 0 deletions
diff --git a/third_party/libwebrtc/rtc_tools/video_file_reader.cc b/third_party/libwebrtc/rtc_tools/video_file_reader.cc
new file mode 100644
index 0000000000..66fed4249e
--- /dev/null
+++ b/third_party/libwebrtc/rtc_tools/video_file_reader.cc
@@ -0,0 +1,288 @@
+/*
+ * 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/video_file_reader.h"
+
+#include <cstdio>
+#include <string>
+#include <vector>
+
+#include "absl/strings/match.h"
+#include "absl/types/optional.h"
+#include "api/make_ref_counted.h"
+#include "api/video/i420_buffer.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/string_encode.h"
+#include "rtc_base/string_to_number.h"
+
+namespace webrtc {
+namespace test {
+
+namespace {
+
+bool ReadBytes(uint8_t* dst, size_t n, FILE* file) {
+ return fread(reinterpret_cast<char*>(dst), /* size= */ 1, n, file) == n;
+}
+
+// Common base class for .yuv and .y4m files.
+class VideoFile : public Video {
+ public:
+ VideoFile(int width,
+ int height,
+ const std::vector<fpos_t>& frame_positions,
+ FILE* file)
+ : width_(width),
+ height_(height),
+ frame_positions_(frame_positions),
+ file_(file) {}
+
+ ~VideoFile() override { fclose(file_); }
+
+ size_t number_of_frames() const override { return frame_positions_.size(); }
+ int width() const override { return width_; }
+ int height() const override { return height_; }
+
+ rtc::scoped_refptr<I420BufferInterface> GetFrame(
+ size_t frame_index) const override {
+ RTC_CHECK_LT(frame_index, frame_positions_.size());
+
+ fsetpos(file_, &frame_positions_[frame_index]);
+ rtc::scoped_refptr<I420Buffer> buffer = I420Buffer::Create(width_, height_);
+
+ if (!ReadBytes(buffer->MutableDataY(), width_ * height_, file_) ||
+ !ReadBytes(buffer->MutableDataU(),
+ buffer->ChromaWidth() * buffer->ChromaHeight(), file_) ||
+ !ReadBytes(buffer->MutableDataV(),
+ buffer->ChromaWidth() * buffer->ChromaHeight(), file_)) {
+ RTC_LOG(LS_ERROR) << "Could not read YUV data for frame " << frame_index;
+ return nullptr;
+ }
+
+ return buffer;
+ }
+
+ private:
+ const int width_;
+ const int height_;
+ const std::vector<fpos_t> frame_positions_;
+ FILE* const file_;
+};
+
+} // namespace
+
+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(rtc::scoped_refptr<const Video>(this), 0);
+}
+
+Video::Iterator Video::end() const {
+ return Iterator(rtc::scoped_refptr<const Video>(this), number_of_frames());
+}
+
+rtc::scoped_refptr<Video> OpenY4mFile(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;
+ if (fscanf(file, "YUV4MPEG2 %n", &parse_file_header_result) != 0 ||
+ 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) {
+ std::array<char, 6> read_buffer;
+ if (fread(read_buffer.data(), 1, read_buffer.size(), file) <
+ read_buffer.size() ||
+ memcmp(read_buffer.data(), "FRAME\n", read_buffer.size()) != 0) {
+ 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 rtc::make_ref_counted<VideoFile>(*width, *height, frame_positions,
+ file);
+}
+
+rtc::scoped_refptr<Video> OpenYuvFile(const std::string& file_name,
+ int width,
+ int height) {
+ 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;
+ }
+
+ 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;
+ }
+
+ // Seek to end of file.
+ fseek(file, 0, SEEK_END);
+ const size_t file_size = ftell(file);
+ // Seek back to beginning of file.
+ fseek(file, 0, SEEK_SET);
+
+ const int i420_frame_size = 3 * width * height / 2;
+ const size_t number_of_frames = file_size / i420_frame_size;
+
+ std::vector<fpos_t> frame_positions;
+ for (size_t i = 0; i < number_of_frames; ++i) {
+ fpos_t pos;
+ fgetpos(file, &pos);
+ frame_positions.push_back(pos);
+ 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 rtc::make_ref_counted<VideoFile>(width, height, frame_positions, file);
+}
+
+rtc::scoped_refptr<Video> OpenYuvOrY4mFile(const std::string& file_name,
+ int width,
+ int height) {
+ if (absl::EndsWith(file_name, ".yuv"))
+ return OpenYuvFile(file_name, width, height);
+ if (absl::EndsWith(file_name, ".y4m"))
+ return OpenY4mFile(file_name);
+
+ RTC_LOG(LS_ERROR) << "Video file does not end in either .yuv or .y4m: "
+ << file_name;
+
+ return nullptr;
+}
+
+} // namespace test
+} // namespace webrtc