xref: /aosp_15_r20/external/webrtc/test/testsupport/yuv_frame_reader.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include <stdio.h>
12 
13 #include <string>
14 
15 #include "api/scoped_refptr.h"
16 #include "api/video/i420_buffer.h"
17 #include "rtc_base/logging.h"
18 #include "test/frame_utils.h"
19 #include "test/testsupport/file_utils.h"
20 #include "test/testsupport/frame_reader.h"
21 
22 namespace webrtc {
23 namespace test {
FrameSizeBytes(int width,int height)24 size_t FrameSizeBytes(int width, int height) {
25   int half_width = (width + 1) / 2;
26   size_t size_y = static_cast<size_t>(width) * height;
27   size_t size_uv = static_cast<size_t>(half_width) * ((height + 1) / 2);
28   return size_y + 2 * size_uv;
29 }
30 
DropperUtil(int source_fps,int target_fps)31 YuvFrameReaderImpl::DropperUtil::DropperUtil(int source_fps, int target_fps)
32     : frame_size_buckets_(
33           std::max(1.0, static_cast<double>(source_fps) / target_fps)),
34       bucket_level_(0.0) {}
35 
36 YuvFrameReaderImpl::DropperUtil::DropDecision
UpdateLevel()37 YuvFrameReaderImpl::DropperUtil::UpdateLevel() {
38   DropDecision decision;
39   if (bucket_level_ <= 0.0) {
40     decision = DropDecision::kKeepFrame;
41     bucket_level_ += frame_size_buckets_;
42   } else {
43     decision = DropDecision::kDropframe;
44   }
45   bucket_level_ -= 1.0;
46   return decision;
47 }
48 
YuvFrameReaderImpl(std::string input_filename,int width,int height)49 YuvFrameReaderImpl::YuvFrameReaderImpl(std::string input_filename,
50                                        int width,
51                                        int height)
52     : YuvFrameReaderImpl(input_filename,
53                          width,
54                          height,
55                          width,
56                          height,
57                          RepeatMode::kSingle,
58                          30,
59                          30) {}
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)60 YuvFrameReaderImpl::YuvFrameReaderImpl(std::string input_filename,
61                                        int input_width,
62                                        int input_height,
63                                        int desired_width,
64                                        int desired_height,
65                                        RepeatMode repeat_mode,
66                                        absl::optional<int> clip_fps,
67                                        int target_fps)
68     : input_filename_(input_filename),
69       frame_length_in_bytes_(input_width * input_height +
70                              2 * ((input_width + 1) / 2) *
71                                  ((input_height + 1) / 2)),
72       input_width_(input_width),
73       input_height_(input_height),
74       desired_width_(desired_width),
75       desired_height_(desired_height),
76       frame_size_bytes_(FrameSizeBytes(input_width, input_height)),
77       repeat_mode_(repeat_mode),
78       number_of_frames_(-1),
79       current_frame_index_(-1),
80       dropper_(clip_fps.has_value() ? new DropperUtil(*clip_fps, target_fps)
81                                     : nullptr),
82       input_file_(nullptr) {}
83 
~YuvFrameReaderImpl()84 YuvFrameReaderImpl::~YuvFrameReaderImpl() {
85   Close();
86 }
87 
Init()88 bool YuvFrameReaderImpl::Init() {
89   if (input_width_ <= 0 || input_height_ <= 0) {
90     RTC_LOG(LS_ERROR) << "Frame width and height must be positive. Was: "
91                       << input_width_ << "x" << input_height_;
92     return false;
93   }
94   input_file_ = fopen(input_filename_.c_str(), "rb");
95   if (input_file_ == nullptr) {
96     RTC_LOG(LS_ERROR) << "Couldn't open input file: "
97                       << input_filename_.c_str();
98     return false;
99   }
100   // Calculate total number of frames.
101   size_t source_file_size = GetFileSize(input_filename_);
102   if (source_file_size <= 0u) {
103     RTC_LOG(LS_ERROR) << "Input file " << input_filename_.c_str()
104                       << " is empty.";
105     return false;
106   }
107   number_of_frames_ =
108       static_cast<int>(source_file_size / frame_length_in_bytes_);
109 
110   if (number_of_frames_ == 0) {
111     RTC_LOG(LS_ERROR) << "Input file " << input_filename_.c_str()
112                       << " is too small.";
113   }
114 
115   current_frame_index_ = 0;
116   return true;
117 }
118 
ReadFrame()119 rtc::scoped_refptr<I420Buffer> YuvFrameReaderImpl::ReadFrame() {
120   if (input_file_ == nullptr) {
121     RTC_LOG(LS_ERROR) << "YuvFrameReaderImpl is not initialized.";
122     return nullptr;
123   }
124 
125   rtc::scoped_refptr<I420Buffer> buffer;
126 
127   do {
128     if (current_frame_index_ >= number_of_frames_) {
129       switch (repeat_mode_) {
130         case RepeatMode::kSingle:
131           return nullptr;
132         case RepeatMode::kRepeat:
133           fseek(input_file_, 0, SEEK_SET);
134           current_frame_index_ = 0;
135           break;
136         case RepeatMode::kPingPong:
137           if (current_frame_index_ == number_of_frames_ * 2) {
138             fseek(input_file_, 0, SEEK_SET);
139             current_frame_index_ = 0;
140           } else {
141             int reverse_frame_index = current_frame_index_ - number_of_frames_;
142             int seek_frame_pos = (number_of_frames_ - reverse_frame_index - 1);
143             fseek(input_file_, seek_frame_pos * frame_size_bytes_, SEEK_SET);
144           }
145           break;
146       }
147     }
148     ++current_frame_index_;
149 
150     buffer = ReadI420Buffer(input_width_, input_height_, input_file_);
151     if (!buffer && ferror(input_file_)) {
152       RTC_LOG(LS_ERROR) << "Couldn't read frame from file: "
153                         << input_filename_.c_str();
154     }
155   } while (dropper_ &&
156            dropper_->UpdateLevel() == DropperUtil::DropDecision::kDropframe);
157 
158   if (input_width_ == desired_width_ && input_height_ == desired_height_) {
159     return buffer;
160   }
161 
162   rtc::scoped_refptr<I420Buffer> rescaled_buffer(
163       I420Buffer::Create(desired_width_, desired_height_));
164   rescaled_buffer->ScaleFrom(*buffer.get());
165 
166   return rescaled_buffer;
167 }
168 
Close()169 void YuvFrameReaderImpl::Close() {
170   if (input_file_ != nullptr) {
171     fclose(input_file_);
172     input_file_ = nullptr;
173   }
174 }
175 
FrameLength()176 size_t YuvFrameReaderImpl::FrameLength() {
177   return frame_length_in_bytes_;
178 }
179 
NumberOfFrames()180 int YuvFrameReaderImpl::NumberOfFrames() {
181   return number_of_frames_;
182 }
183 
184 }  // namespace test
185 }  // namespace webrtc
186