Home | History | Annotate | Download | only in test
      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:
     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 
     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:
     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 
     76   virtual ~YuvFileGenerator() {
     77     for (FILE* file : files_)
     78       fclose(file);
     79   }
     80 
     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 
     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:
    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 
    163   virtual ~ScrollingImageFrameGenerator() {}
    164 
    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 
    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 
    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 
    236 FrameGenerator* FrameGenerator::CreateChromaGenerator(size_t width,
    237                                                       size_t height) {
    238   return new ChromaGenerator(width, height);
    239 }
    240 
    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 
    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