• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Copyright (c) 2013 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 #include "webrtc/test/frame_generator.h"
11 
12 #include <math.h>
13 #include <stdio.h>
14 #include <string.h>
15 
16 #include "webrtc/base/checks.h"
17 #include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
18 #include "webrtc/system_wrappers/include/clock.h"
19 
20 namespace webrtc {
21 namespace test {
22 namespace {
23 
24 class ChromaGenerator : public FrameGenerator {
25  public:
ChromaGenerator(size_t width,size_t height)26   ChromaGenerator(size_t width, size_t height)
27       : angle_(0.0), width_(width), height_(height) {
28     assert(width > 0);
29     assert(height > 0);
30   }
31 
NextFrame()32   VideoFrame* NextFrame() override {
33     frame_.CreateEmptyFrame(static_cast<int>(width_),
34                             static_cast<int>(height_),
35                             static_cast<int>(width_),
36                             static_cast<int>((width_ + 1) / 2),
37                             static_cast<int>((width_ + 1) / 2));
38     angle_ += 30.0;
39     uint8_t u = fabs(sin(angle_)) * 0xFF;
40     uint8_t v = fabs(cos(angle_)) * 0xFF;
41 
42     memset(frame_.buffer(kYPlane), 0x80, frame_.allocated_size(kYPlane));
43     memset(frame_.buffer(kUPlane), u, frame_.allocated_size(kUPlane));
44     memset(frame_.buffer(kVPlane), v, frame_.allocated_size(kVPlane));
45     return &frame_;
46   }
47 
48  private:
49   double angle_;
50   size_t width_;
51   size_t height_;
52   VideoFrame frame_;
53 };
54 
55 class YuvFileGenerator : public FrameGenerator {
56  public:
YuvFileGenerator(std::vector<FILE * > files,size_t width,size_t height,int frame_repeat_count)57   YuvFileGenerator(std::vector<FILE*> files,
58                    size_t width,
59                    size_t height,
60                    int frame_repeat_count)
61       : file_index_(0),
62         files_(files),
63         width_(width),
64         height_(height),
65         frame_size_(CalcBufferSize(kI420,
66                                    static_cast<int>(width_),
67                                    static_cast<int>(height_))),
68         frame_buffer_(new uint8_t[frame_size_]),
69         frame_display_count_(frame_repeat_count),
70         current_display_count_(0) {
71     assert(width > 0);
72     assert(height > 0);
73     assert(frame_repeat_count > 0);
74   }
75 
~YuvFileGenerator()76   virtual ~YuvFileGenerator() {
77     for (FILE* file : files_)
78       fclose(file);
79   }
80 
NextFrame()81   VideoFrame* NextFrame() override {
82     if (current_display_count_ == 0)
83       ReadNextFrame();
84     if (++current_display_count_ >= frame_display_count_)
85       current_display_count_ = 0;
86 
87     // If this is the last repeatition of this frame, it's OK to use the
88     // original instance, otherwise use a copy.
89     if (current_display_count_ == frame_display_count_)
90       return &last_read_frame_;
91 
92     temp_frame_copy_.CopyFrame(last_read_frame_);
93     return &temp_frame_copy_;
94   }
95 
ReadNextFrame()96   void ReadNextFrame() {
97     size_t bytes_read =
98         fread(frame_buffer_.get(), 1, frame_size_, files_[file_index_]);
99     if (bytes_read < frame_size_) {
100       // No more frames to read in this file, rewind and move to next file.
101       rewind(files_[file_index_]);
102       file_index_ = (file_index_ + 1) % files_.size();
103       bytes_read = fread(frame_buffer_.get(), 1, frame_size_,
104           files_[file_index_]);
105       assert(bytes_read >= frame_size_);
106     }
107 
108     last_read_frame_.CreateEmptyFrame(
109         static_cast<int>(width_), static_cast<int>(height_),
110         static_cast<int>(width_), static_cast<int>((width_ + 1) / 2),
111         static_cast<int>((width_ + 1) / 2));
112 
113     ConvertToI420(kI420, frame_buffer_.get(), 0, 0, static_cast<int>(width_),
114                   static_cast<int>(height_), 0, kVideoRotation_0,
115                   &last_read_frame_);
116   }
117 
118  private:
119   size_t file_index_;
120   const std::vector<FILE*> files_;
121   const size_t width_;
122   const size_t height_;
123   const size_t frame_size_;
124   const rtc::scoped_ptr<uint8_t[]> frame_buffer_;
125   const int frame_display_count_;
126   int current_display_count_;
127   VideoFrame last_read_frame_;
128   VideoFrame temp_frame_copy_;
129 };
130 
131 class ScrollingImageFrameGenerator : public FrameGenerator {
132  public:
ScrollingImageFrameGenerator(Clock * clock,const std::vector<FILE * > & files,size_t source_width,size_t source_height,size_t target_width,size_t target_height,int64_t scroll_time_ms,int64_t pause_time_ms)133   ScrollingImageFrameGenerator(Clock* clock,
134                                const std::vector<FILE*>& files,
135                                size_t source_width,
136                                size_t source_height,
137                                size_t target_width,
138                                size_t target_height,
139                                int64_t scroll_time_ms,
140                                int64_t pause_time_ms)
141       : clock_(clock),
142         start_time_(clock->TimeInMilliseconds()),
143         scroll_time_(scroll_time_ms),
144         pause_time_(pause_time_ms),
145         num_frames_(files.size()),
146         current_frame_num_(num_frames_ - 1),
147         current_source_frame_(nullptr),
148         file_generator_(files, source_width, source_height, 1) {
149     RTC_DCHECK(clock_ != nullptr);
150     RTC_DCHECK_GT(num_frames_, 0u);
151     RTC_DCHECK_GE(source_height, target_height);
152     RTC_DCHECK_GE(source_width, target_width);
153     RTC_DCHECK_GE(scroll_time_ms, 0);
154     RTC_DCHECK_GE(pause_time_ms, 0);
155     RTC_DCHECK_GT(scroll_time_ms + pause_time_ms, 0);
156     current_frame_.CreateEmptyFrame(static_cast<int>(target_width),
157                                     static_cast<int>(target_height),
158                                     static_cast<int>(target_width),
159                                     static_cast<int>((target_width + 1) / 2),
160                                     static_cast<int>((target_width + 1) / 2));
161   }
162 
~ScrollingImageFrameGenerator()163   virtual ~ScrollingImageFrameGenerator() {}
164 
NextFrame()165   VideoFrame* NextFrame() override {
166     const int64_t kFrameDisplayTime = scroll_time_ + pause_time_;
167     const int64_t now = clock_->TimeInMilliseconds();
168     int64_t ms_since_start = now - start_time_;
169 
170     size_t frame_num = (ms_since_start / kFrameDisplayTime) % num_frames_;
171     UpdateSourceFrame(frame_num);
172 
173     double scroll_factor;
174     int64_t time_into_frame = ms_since_start % kFrameDisplayTime;
175     if (time_into_frame < scroll_time_) {
176       scroll_factor = static_cast<double>(time_into_frame) / scroll_time_;
177     } else {
178       scroll_factor = 1.0;
179     }
180     CropSourceToScrolledImage(scroll_factor);
181 
182     return &current_frame_;
183   }
184 
UpdateSourceFrame(size_t frame_num)185   void UpdateSourceFrame(size_t frame_num) {
186     while (current_frame_num_ != frame_num) {
187       current_source_frame_ = file_generator_.NextFrame();
188       current_frame_num_ = (current_frame_num_ + 1) % num_frames_;
189     }
190     RTC_DCHECK(current_source_frame_ != nullptr);
191   }
192 
CropSourceToScrolledImage(double scroll_factor)193   void CropSourceToScrolledImage(double scroll_factor) {
194     const int kTargetWidth = current_frame_.width();
195     const int kTargetHeight = current_frame_.height();
196     int scroll_margin_x = current_source_frame_->width() - kTargetWidth;
197     int pixels_scrolled_x =
198         static_cast<int>(scroll_margin_x * scroll_factor + 0.5);
199     int scroll_margin_y = current_source_frame_->height() - kTargetHeight;
200     int pixels_scrolled_y =
201         static_cast<int>(scroll_margin_y * scroll_factor + 0.5);
202 
203     int offset_y = (current_source_frame_->stride(PlaneType::kYPlane) *
204                     pixels_scrolled_y) +
205                    pixels_scrolled_x;
206     int offset_u = (current_source_frame_->stride(PlaneType::kUPlane) *
207                     (pixels_scrolled_y / 2)) +
208                    (pixels_scrolled_x / 2);
209     int offset_v = (current_source_frame_->stride(PlaneType::kVPlane) *
210                     (pixels_scrolled_y / 2)) +
211                    (pixels_scrolled_x / 2);
212 
213     current_frame_.CreateFrame(
214         &current_source_frame_->buffer(PlaneType::kYPlane)[offset_y],
215         &current_source_frame_->buffer(PlaneType::kUPlane)[offset_u],
216         &current_source_frame_->buffer(PlaneType::kVPlane)[offset_v],
217         kTargetWidth, kTargetHeight,
218         current_source_frame_->stride(PlaneType::kYPlane),
219         current_source_frame_->stride(PlaneType::kUPlane),
220         current_source_frame_->stride(PlaneType::kVPlane));
221   }
222 
223   Clock* const clock_;
224   const int64_t start_time_;
225   const int64_t scroll_time_;
226   const int64_t pause_time_;
227   const size_t num_frames_;
228   size_t current_frame_num_;
229   VideoFrame* current_source_frame_;
230   VideoFrame current_frame_;
231   YuvFileGenerator file_generator_;
232 };
233 
234 }  // namespace
235 
CreateChromaGenerator(size_t width,size_t height)236 FrameGenerator* FrameGenerator::CreateChromaGenerator(size_t width,
237                                                       size_t height) {
238   return new ChromaGenerator(width, height);
239 }
240 
CreateFromYuvFile(std::vector<std::string> filenames,size_t width,size_t height,int frame_repeat_count)241 FrameGenerator* FrameGenerator::CreateFromYuvFile(
242     std::vector<std::string> filenames,
243     size_t width,
244     size_t height,
245     int frame_repeat_count) {
246   assert(!filenames.empty());
247   std::vector<FILE*> files;
248   for (const std::string& filename : filenames) {
249     FILE* file = fopen(filename.c_str(), "rb");
250     RTC_DCHECK(file != nullptr);
251     files.push_back(file);
252   }
253 
254   return new YuvFileGenerator(files, width, height, frame_repeat_count);
255 }
256 
CreateScrollingInputFromYuvFiles(Clock * clock,std::vector<std::string> filenames,size_t source_width,size_t source_height,size_t target_width,size_t target_height,int64_t scroll_time_ms,int64_t pause_time_ms)257 FrameGenerator* FrameGenerator::CreateScrollingInputFromYuvFiles(
258     Clock* clock,
259     std::vector<std::string> filenames,
260     size_t source_width,
261     size_t source_height,
262     size_t target_width,
263     size_t target_height,
264     int64_t scroll_time_ms,
265     int64_t pause_time_ms) {
266   assert(!filenames.empty());
267   std::vector<FILE*> files;
268   for (const std::string& filename : filenames) {
269     FILE* file = fopen(filename.c_str(), "rb");
270     RTC_DCHECK(file != nullptr);
271     files.push_back(file);
272   }
273 
274   return new ScrollingImageFrameGenerator(
275       clock, files, source_width, source_height, target_width, target_height,
276       scroll_time_ms, pause_time_ms);
277 }
278 
279 }  // namespace test
280 }  // namespace webrtc
281