1*d9f75844SAndroid Build Coastguard Worker /*
2*d9f75844SAndroid Build Coastguard Worker * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3*d9f75844SAndroid Build Coastguard Worker *
4*d9f75844SAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license
5*d9f75844SAndroid Build Coastguard Worker * that can be found in the LICENSE file in the root of the source
6*d9f75844SAndroid Build Coastguard Worker * tree. An additional intellectual property rights grant can be found
7*d9f75844SAndroid Build Coastguard Worker * in the file PATENTS. All contributing project authors may
8*d9f75844SAndroid Build Coastguard Worker * be found in the AUTHORS file in the root of the source tree.
9*d9f75844SAndroid Build Coastguard Worker */
10*d9f75844SAndroid Build Coastguard Worker #include "test/frame_generator.h"
11*d9f75844SAndroid Build Coastguard Worker
12*d9f75844SAndroid Build Coastguard Worker #include <string.h>
13*d9f75844SAndroid Build Coastguard Worker
14*d9f75844SAndroid Build Coastguard Worker #include <cstdint>
15*d9f75844SAndroid Build Coastguard Worker #include <cstdio>
16*d9f75844SAndroid Build Coastguard Worker #include <memory>
17*d9f75844SAndroid Build Coastguard Worker
18*d9f75844SAndroid Build Coastguard Worker #include "api/video/i010_buffer.h"
19*d9f75844SAndroid Build Coastguard Worker #include "api/video/nv12_buffer.h"
20*d9f75844SAndroid Build Coastguard Worker #include "api/video/video_rotation.h"
21*d9f75844SAndroid Build Coastguard Worker #include "common_video/include/video_frame_buffer.h"
22*d9f75844SAndroid Build Coastguard Worker #include "common_video/libyuv/include/webrtc_libyuv.h"
23*d9f75844SAndroid Build Coastguard Worker #include "rtc_base/checks.h"
24*d9f75844SAndroid Build Coastguard Worker #include "test/frame_utils.h"
25*d9f75844SAndroid Build Coastguard Worker
26*d9f75844SAndroid Build Coastguard Worker namespace webrtc {
27*d9f75844SAndroid Build Coastguard Worker namespace test {
28*d9f75844SAndroid Build Coastguard Worker
SquareGenerator(int width,int height,OutputType type,int num_squares)29*d9f75844SAndroid Build Coastguard Worker SquareGenerator::SquareGenerator(int width,
30*d9f75844SAndroid Build Coastguard Worker int height,
31*d9f75844SAndroid Build Coastguard Worker OutputType type,
32*d9f75844SAndroid Build Coastguard Worker int num_squares)
33*d9f75844SAndroid Build Coastguard Worker : type_(type) {
34*d9f75844SAndroid Build Coastguard Worker ChangeResolution(width, height);
35*d9f75844SAndroid Build Coastguard Worker for (int i = 0; i < num_squares; ++i) {
36*d9f75844SAndroid Build Coastguard Worker squares_.emplace_back(new Square(width, height, i + 1));
37*d9f75844SAndroid Build Coastguard Worker }
38*d9f75844SAndroid Build Coastguard Worker }
39*d9f75844SAndroid Build Coastguard Worker
ChangeResolution(size_t width,size_t height)40*d9f75844SAndroid Build Coastguard Worker void SquareGenerator::ChangeResolution(size_t width, size_t height) {
41*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
42*d9f75844SAndroid Build Coastguard Worker width_ = static_cast<int>(width);
43*d9f75844SAndroid Build Coastguard Worker height_ = static_cast<int>(height);
44*d9f75844SAndroid Build Coastguard Worker RTC_CHECK(width_ > 0);
45*d9f75844SAndroid Build Coastguard Worker RTC_CHECK(height_ > 0);
46*d9f75844SAndroid Build Coastguard Worker }
47*d9f75844SAndroid Build Coastguard Worker
CreateI420Buffer(int width,int height)48*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<I420Buffer> SquareGenerator::CreateI420Buffer(int width,
49*d9f75844SAndroid Build Coastguard Worker int height) {
50*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<I420Buffer> buffer(I420Buffer::Create(width, height));
51*d9f75844SAndroid Build Coastguard Worker memset(buffer->MutableDataY(), 127, height * buffer->StrideY());
52*d9f75844SAndroid Build Coastguard Worker memset(buffer->MutableDataU(), 127,
53*d9f75844SAndroid Build Coastguard Worker buffer->ChromaHeight() * buffer->StrideU());
54*d9f75844SAndroid Build Coastguard Worker memset(buffer->MutableDataV(), 127,
55*d9f75844SAndroid Build Coastguard Worker buffer->ChromaHeight() * buffer->StrideV());
56*d9f75844SAndroid Build Coastguard Worker return buffer;
57*d9f75844SAndroid Build Coastguard Worker }
58*d9f75844SAndroid Build Coastguard Worker
NextFrame()59*d9f75844SAndroid Build Coastguard Worker FrameGeneratorInterface::VideoFrameData SquareGenerator::NextFrame() {
60*d9f75844SAndroid Build Coastguard Worker MutexLock lock(&mutex_);
61*d9f75844SAndroid Build Coastguard Worker
62*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<VideoFrameBuffer> buffer = nullptr;
63*d9f75844SAndroid Build Coastguard Worker switch (type_) {
64*d9f75844SAndroid Build Coastguard Worker case OutputType::kI420:
65*d9f75844SAndroid Build Coastguard Worker case OutputType::kI010:
66*d9f75844SAndroid Build Coastguard Worker case OutputType::kNV12: {
67*d9f75844SAndroid Build Coastguard Worker buffer = CreateI420Buffer(width_, height_);
68*d9f75844SAndroid Build Coastguard Worker break;
69*d9f75844SAndroid Build Coastguard Worker }
70*d9f75844SAndroid Build Coastguard Worker case OutputType::kI420A: {
71*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<I420Buffer> yuv_buffer =
72*d9f75844SAndroid Build Coastguard Worker CreateI420Buffer(width_, height_);
73*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<I420Buffer> axx_buffer =
74*d9f75844SAndroid Build Coastguard Worker CreateI420Buffer(width_, height_);
75*d9f75844SAndroid Build Coastguard Worker buffer = WrapI420ABuffer(yuv_buffer->width(), yuv_buffer->height(),
76*d9f75844SAndroid Build Coastguard Worker yuv_buffer->DataY(), yuv_buffer->StrideY(),
77*d9f75844SAndroid Build Coastguard Worker yuv_buffer->DataU(), yuv_buffer->StrideU(),
78*d9f75844SAndroid Build Coastguard Worker yuv_buffer->DataV(), yuv_buffer->StrideV(),
79*d9f75844SAndroid Build Coastguard Worker axx_buffer->DataY(), axx_buffer->StrideY(),
80*d9f75844SAndroid Build Coastguard Worker // To keep references alive.
81*d9f75844SAndroid Build Coastguard Worker [yuv_buffer, axx_buffer] {});
82*d9f75844SAndroid Build Coastguard Worker break;
83*d9f75844SAndroid Build Coastguard Worker }
84*d9f75844SAndroid Build Coastguard Worker default:
85*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_NOTREACHED() << "The given output format is not supported.";
86*d9f75844SAndroid Build Coastguard Worker }
87*d9f75844SAndroid Build Coastguard Worker
88*d9f75844SAndroid Build Coastguard Worker for (const auto& square : squares_)
89*d9f75844SAndroid Build Coastguard Worker square->Draw(buffer);
90*d9f75844SAndroid Build Coastguard Worker
91*d9f75844SAndroid Build Coastguard Worker if (type_ == OutputType::kI010) {
92*d9f75844SAndroid Build Coastguard Worker buffer = I010Buffer::Copy(*buffer->ToI420());
93*d9f75844SAndroid Build Coastguard Worker } else if (type_ == OutputType::kNV12) {
94*d9f75844SAndroid Build Coastguard Worker buffer = NV12Buffer::Copy(*buffer->ToI420());
95*d9f75844SAndroid Build Coastguard Worker }
96*d9f75844SAndroid Build Coastguard Worker
97*d9f75844SAndroid Build Coastguard Worker return VideoFrameData(buffer, absl::nullopt);
98*d9f75844SAndroid Build Coastguard Worker }
99*d9f75844SAndroid Build Coastguard Worker
Square(int width,int height,int seed)100*d9f75844SAndroid Build Coastguard Worker SquareGenerator::Square::Square(int width, int height, int seed)
101*d9f75844SAndroid Build Coastguard Worker : random_generator_(seed),
102*d9f75844SAndroid Build Coastguard Worker x_(random_generator_.Rand(0, width)),
103*d9f75844SAndroid Build Coastguard Worker y_(random_generator_.Rand(0, height)),
104*d9f75844SAndroid Build Coastguard Worker length_(random_generator_.Rand(1, width > 4 ? width / 4 : 1)),
105*d9f75844SAndroid Build Coastguard Worker yuv_y_(random_generator_.Rand(0, 255)),
106*d9f75844SAndroid Build Coastguard Worker yuv_u_(random_generator_.Rand(0, 255)),
107*d9f75844SAndroid Build Coastguard Worker yuv_v_(random_generator_.Rand(0, 255)),
108*d9f75844SAndroid Build Coastguard Worker yuv_a_(random_generator_.Rand(0, 255)) {}
109*d9f75844SAndroid Build Coastguard Worker
Draw(const rtc::scoped_refptr<VideoFrameBuffer> & frame_buffer)110*d9f75844SAndroid Build Coastguard Worker void SquareGenerator::Square::Draw(
111*d9f75844SAndroid Build Coastguard Worker const rtc::scoped_refptr<VideoFrameBuffer>& frame_buffer) {
112*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(frame_buffer->type() == VideoFrameBuffer::Type::kI420 ||
113*d9f75844SAndroid Build Coastguard Worker frame_buffer->type() == VideoFrameBuffer::Type::kI420A);
114*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<I420BufferInterface> buffer = frame_buffer->ToI420();
115*d9f75844SAndroid Build Coastguard Worker int length_cap = std::min(buffer->height(), buffer->width()) / 4;
116*d9f75844SAndroid Build Coastguard Worker int length = std::min(length_, length_cap);
117*d9f75844SAndroid Build Coastguard Worker x_ = (x_ + random_generator_.Rand(0, 4)) % (buffer->width() - length);
118*d9f75844SAndroid Build Coastguard Worker y_ = (y_ + random_generator_.Rand(0, 4)) % (buffer->height() - length);
119*d9f75844SAndroid Build Coastguard Worker for (int y = y_; y < y_ + length; ++y) {
120*d9f75844SAndroid Build Coastguard Worker uint8_t* pos_y =
121*d9f75844SAndroid Build Coastguard Worker (const_cast<uint8_t*>(buffer->DataY()) + x_ + y * buffer->StrideY());
122*d9f75844SAndroid Build Coastguard Worker memset(pos_y, yuv_y_, length);
123*d9f75844SAndroid Build Coastguard Worker }
124*d9f75844SAndroid Build Coastguard Worker
125*d9f75844SAndroid Build Coastguard Worker for (int y = y_; y < y_ + length; y = y + 2) {
126*d9f75844SAndroid Build Coastguard Worker uint8_t* pos_u = (const_cast<uint8_t*>(buffer->DataU()) + x_ / 2 +
127*d9f75844SAndroid Build Coastguard Worker y / 2 * buffer->StrideU());
128*d9f75844SAndroid Build Coastguard Worker memset(pos_u, yuv_u_, length / 2);
129*d9f75844SAndroid Build Coastguard Worker uint8_t* pos_v = (const_cast<uint8_t*>(buffer->DataV()) + x_ / 2 +
130*d9f75844SAndroid Build Coastguard Worker y / 2 * buffer->StrideV());
131*d9f75844SAndroid Build Coastguard Worker memset(pos_v, yuv_v_, length / 2);
132*d9f75844SAndroid Build Coastguard Worker }
133*d9f75844SAndroid Build Coastguard Worker
134*d9f75844SAndroid Build Coastguard Worker if (frame_buffer->type() == VideoFrameBuffer::Type::kI420)
135*d9f75844SAndroid Build Coastguard Worker return;
136*d9f75844SAndroid Build Coastguard Worker
137*d9f75844SAndroid Build Coastguard Worker // Optionally draw on alpha plane if given.
138*d9f75844SAndroid Build Coastguard Worker const webrtc::I420ABufferInterface* yuva_buffer = frame_buffer->GetI420A();
139*d9f75844SAndroid Build Coastguard Worker for (int y = y_; y < y_ + length; ++y) {
140*d9f75844SAndroid Build Coastguard Worker uint8_t* pos_y = (const_cast<uint8_t*>(yuva_buffer->DataA()) + x_ +
141*d9f75844SAndroid Build Coastguard Worker y * yuva_buffer->StrideA());
142*d9f75844SAndroid Build Coastguard Worker memset(pos_y, yuv_a_, length);
143*d9f75844SAndroid Build Coastguard Worker }
144*d9f75844SAndroid Build Coastguard Worker }
145*d9f75844SAndroid Build Coastguard Worker
YuvFileGenerator(std::vector<FILE * > files,size_t width,size_t height,int frame_repeat_count)146*d9f75844SAndroid Build Coastguard Worker YuvFileGenerator::YuvFileGenerator(std::vector<FILE*> files,
147*d9f75844SAndroid Build Coastguard Worker size_t width,
148*d9f75844SAndroid Build Coastguard Worker size_t height,
149*d9f75844SAndroid Build Coastguard Worker int frame_repeat_count)
150*d9f75844SAndroid Build Coastguard Worker : file_index_(0),
151*d9f75844SAndroid Build Coastguard Worker frame_index_(std::numeric_limits<size_t>::max()),
152*d9f75844SAndroid Build Coastguard Worker files_(files),
153*d9f75844SAndroid Build Coastguard Worker width_(width),
154*d9f75844SAndroid Build Coastguard Worker height_(height),
155*d9f75844SAndroid Build Coastguard Worker frame_size_(CalcBufferSize(VideoType::kI420,
156*d9f75844SAndroid Build Coastguard Worker static_cast<int>(width_),
157*d9f75844SAndroid Build Coastguard Worker static_cast<int>(height_))),
158*d9f75844SAndroid Build Coastguard Worker frame_buffer_(new uint8_t[frame_size_]),
159*d9f75844SAndroid Build Coastguard Worker frame_display_count_(frame_repeat_count),
160*d9f75844SAndroid Build Coastguard Worker current_display_count_(0) {
161*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(width, 0);
162*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(height, 0);
163*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(frame_repeat_count, 0);
164*d9f75844SAndroid Build Coastguard Worker }
165*d9f75844SAndroid Build Coastguard Worker
~YuvFileGenerator()166*d9f75844SAndroid Build Coastguard Worker YuvFileGenerator::~YuvFileGenerator() {
167*d9f75844SAndroid Build Coastguard Worker for (FILE* file : files_)
168*d9f75844SAndroid Build Coastguard Worker fclose(file);
169*d9f75844SAndroid Build Coastguard Worker }
170*d9f75844SAndroid Build Coastguard Worker
NextFrame()171*d9f75844SAndroid Build Coastguard Worker FrameGeneratorInterface::VideoFrameData YuvFileGenerator::NextFrame() {
172*d9f75844SAndroid Build Coastguard Worker // Empty update by default.
173*d9f75844SAndroid Build Coastguard Worker VideoFrame::UpdateRect update_rect{0, 0, 0, 0};
174*d9f75844SAndroid Build Coastguard Worker if (current_display_count_ == 0) {
175*d9f75844SAndroid Build Coastguard Worker const bool got_new_frame = ReadNextFrame();
176*d9f75844SAndroid Build Coastguard Worker // Full update on a new frame from file.
177*d9f75844SAndroid Build Coastguard Worker if (got_new_frame) {
178*d9f75844SAndroid Build Coastguard Worker update_rect = VideoFrame::UpdateRect{0, 0, static_cast<int>(width_),
179*d9f75844SAndroid Build Coastguard Worker static_cast<int>(height_)};
180*d9f75844SAndroid Build Coastguard Worker }
181*d9f75844SAndroid Build Coastguard Worker }
182*d9f75844SAndroid Build Coastguard Worker if (++current_display_count_ >= frame_display_count_)
183*d9f75844SAndroid Build Coastguard Worker current_display_count_ = 0;
184*d9f75844SAndroid Build Coastguard Worker
185*d9f75844SAndroid Build Coastguard Worker return VideoFrameData(last_read_buffer_, update_rect);
186*d9f75844SAndroid Build Coastguard Worker }
187*d9f75844SAndroid Build Coastguard Worker
ReadNextFrame()188*d9f75844SAndroid Build Coastguard Worker bool YuvFileGenerator::ReadNextFrame() {
189*d9f75844SAndroid Build Coastguard Worker size_t prev_frame_index = frame_index_;
190*d9f75844SAndroid Build Coastguard Worker size_t prev_file_index = file_index_;
191*d9f75844SAndroid Build Coastguard Worker last_read_buffer_ = test::ReadI420Buffer(
192*d9f75844SAndroid Build Coastguard Worker static_cast<int>(width_), static_cast<int>(height_), files_[file_index_]);
193*d9f75844SAndroid Build Coastguard Worker ++frame_index_;
194*d9f75844SAndroid Build Coastguard Worker if (!last_read_buffer_) {
195*d9f75844SAndroid Build Coastguard Worker // No more frames to read in this file, rewind and move to next file.
196*d9f75844SAndroid Build Coastguard Worker rewind(files_[file_index_]);
197*d9f75844SAndroid Build Coastguard Worker
198*d9f75844SAndroid Build Coastguard Worker frame_index_ = 0;
199*d9f75844SAndroid Build Coastguard Worker file_index_ = (file_index_ + 1) % files_.size();
200*d9f75844SAndroid Build Coastguard Worker last_read_buffer_ =
201*d9f75844SAndroid Build Coastguard Worker test::ReadI420Buffer(static_cast<int>(width_),
202*d9f75844SAndroid Build Coastguard Worker static_cast<int>(height_), files_[file_index_]);
203*d9f75844SAndroid Build Coastguard Worker RTC_CHECK(last_read_buffer_);
204*d9f75844SAndroid Build Coastguard Worker }
205*d9f75844SAndroid Build Coastguard Worker return frame_index_ != prev_frame_index || file_index_ != prev_file_index;
206*d9f75844SAndroid Build Coastguard Worker }
207*d9f75844SAndroid Build Coastguard Worker
NV12FileGenerator(std::vector<FILE * > files,size_t width,size_t height,int frame_repeat_count)208*d9f75844SAndroid Build Coastguard Worker NV12FileGenerator::NV12FileGenerator(std::vector<FILE*> files,
209*d9f75844SAndroid Build Coastguard Worker size_t width,
210*d9f75844SAndroid Build Coastguard Worker size_t height,
211*d9f75844SAndroid Build Coastguard Worker int frame_repeat_count)
212*d9f75844SAndroid Build Coastguard Worker : file_index_(0),
213*d9f75844SAndroid Build Coastguard Worker frame_index_(std::numeric_limits<size_t>::max()),
214*d9f75844SAndroid Build Coastguard Worker files_(files),
215*d9f75844SAndroid Build Coastguard Worker width_(width),
216*d9f75844SAndroid Build Coastguard Worker height_(height),
217*d9f75844SAndroid Build Coastguard Worker frame_size_(CalcBufferSize(VideoType::kNV12,
218*d9f75844SAndroid Build Coastguard Worker static_cast<int>(width_),
219*d9f75844SAndroid Build Coastguard Worker static_cast<int>(height_))),
220*d9f75844SAndroid Build Coastguard Worker frame_buffer_(new uint8_t[frame_size_]),
221*d9f75844SAndroid Build Coastguard Worker frame_display_count_(frame_repeat_count),
222*d9f75844SAndroid Build Coastguard Worker current_display_count_(0) {
223*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(width, 0);
224*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(height, 0);
225*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(frame_repeat_count, 0);
226*d9f75844SAndroid Build Coastguard Worker }
227*d9f75844SAndroid Build Coastguard Worker
~NV12FileGenerator()228*d9f75844SAndroid Build Coastguard Worker NV12FileGenerator::~NV12FileGenerator() {
229*d9f75844SAndroid Build Coastguard Worker for (FILE* file : files_)
230*d9f75844SAndroid Build Coastguard Worker fclose(file);
231*d9f75844SAndroid Build Coastguard Worker }
232*d9f75844SAndroid Build Coastguard Worker
NextFrame()233*d9f75844SAndroid Build Coastguard Worker FrameGeneratorInterface::VideoFrameData NV12FileGenerator::NextFrame() {
234*d9f75844SAndroid Build Coastguard Worker // Empty update by default.
235*d9f75844SAndroid Build Coastguard Worker VideoFrame::UpdateRect update_rect{0, 0, 0, 0};
236*d9f75844SAndroid Build Coastguard Worker if (current_display_count_ == 0) {
237*d9f75844SAndroid Build Coastguard Worker const bool got_new_frame = ReadNextFrame();
238*d9f75844SAndroid Build Coastguard Worker // Full update on a new frame from file.
239*d9f75844SAndroid Build Coastguard Worker if (got_new_frame) {
240*d9f75844SAndroid Build Coastguard Worker update_rect = VideoFrame::UpdateRect{0, 0, static_cast<int>(width_),
241*d9f75844SAndroid Build Coastguard Worker static_cast<int>(height_)};
242*d9f75844SAndroid Build Coastguard Worker }
243*d9f75844SAndroid Build Coastguard Worker }
244*d9f75844SAndroid Build Coastguard Worker if (++current_display_count_ >= frame_display_count_)
245*d9f75844SAndroid Build Coastguard Worker current_display_count_ = 0;
246*d9f75844SAndroid Build Coastguard Worker
247*d9f75844SAndroid Build Coastguard Worker return VideoFrameData(last_read_buffer_, update_rect);
248*d9f75844SAndroid Build Coastguard Worker }
249*d9f75844SAndroid Build Coastguard Worker
ReadNextFrame()250*d9f75844SAndroid Build Coastguard Worker bool NV12FileGenerator::ReadNextFrame() {
251*d9f75844SAndroid Build Coastguard Worker size_t prev_frame_index = frame_index_;
252*d9f75844SAndroid Build Coastguard Worker size_t prev_file_index = file_index_;
253*d9f75844SAndroid Build Coastguard Worker last_read_buffer_ = test::ReadNV12Buffer(
254*d9f75844SAndroid Build Coastguard Worker static_cast<int>(width_), static_cast<int>(height_), files_[file_index_]);
255*d9f75844SAndroid Build Coastguard Worker ++frame_index_;
256*d9f75844SAndroid Build Coastguard Worker if (!last_read_buffer_) {
257*d9f75844SAndroid Build Coastguard Worker // No more frames to read in this file, rewind and move to next file.
258*d9f75844SAndroid Build Coastguard Worker rewind(files_[file_index_]);
259*d9f75844SAndroid Build Coastguard Worker
260*d9f75844SAndroid Build Coastguard Worker frame_index_ = 0;
261*d9f75844SAndroid Build Coastguard Worker file_index_ = (file_index_ + 1) % files_.size();
262*d9f75844SAndroid Build Coastguard Worker last_read_buffer_ =
263*d9f75844SAndroid Build Coastguard Worker test::ReadNV12Buffer(static_cast<int>(width_),
264*d9f75844SAndroid Build Coastguard Worker static_cast<int>(height_), files_[file_index_]);
265*d9f75844SAndroid Build Coastguard Worker RTC_CHECK(last_read_buffer_);
266*d9f75844SAndroid Build Coastguard Worker }
267*d9f75844SAndroid Build Coastguard Worker return frame_index_ != prev_frame_index || file_index_ != prev_file_index;
268*d9f75844SAndroid Build Coastguard Worker }
269*d9f75844SAndroid Build Coastguard Worker
SlideGenerator(int width,int height,int frame_repeat_count)270*d9f75844SAndroid Build Coastguard Worker SlideGenerator::SlideGenerator(int width, int height, int frame_repeat_count)
271*d9f75844SAndroid Build Coastguard Worker : width_(width),
272*d9f75844SAndroid Build Coastguard Worker height_(height),
273*d9f75844SAndroid Build Coastguard Worker frame_display_count_(frame_repeat_count),
274*d9f75844SAndroid Build Coastguard Worker current_display_count_(0),
275*d9f75844SAndroid Build Coastguard Worker random_generator_(1234) {
276*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(width, 0);
277*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(height, 0);
278*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(frame_repeat_count, 0);
279*d9f75844SAndroid Build Coastguard Worker }
280*d9f75844SAndroid Build Coastguard Worker
NextFrame()281*d9f75844SAndroid Build Coastguard Worker FrameGeneratorInterface::VideoFrameData SlideGenerator::NextFrame() {
282*d9f75844SAndroid Build Coastguard Worker if (current_display_count_ == 0)
283*d9f75844SAndroid Build Coastguard Worker GenerateNewFrame();
284*d9f75844SAndroid Build Coastguard Worker if (++current_display_count_ >= frame_display_count_)
285*d9f75844SAndroid Build Coastguard Worker current_display_count_ = 0;
286*d9f75844SAndroid Build Coastguard Worker
287*d9f75844SAndroid Build Coastguard Worker return VideoFrameData(buffer_, absl::nullopt);
288*d9f75844SAndroid Build Coastguard Worker }
289*d9f75844SAndroid Build Coastguard Worker
GenerateNewFrame()290*d9f75844SAndroid Build Coastguard Worker void SlideGenerator::GenerateNewFrame() {
291*d9f75844SAndroid Build Coastguard Worker // The squares should have a varying order of magnitude in order
292*d9f75844SAndroid Build Coastguard Worker // to simulate variation in the slides' complexity.
293*d9f75844SAndroid Build Coastguard Worker const int kSquareNum = 1 << (4 + (random_generator_.Rand(0, 3) * 2));
294*d9f75844SAndroid Build Coastguard Worker
295*d9f75844SAndroid Build Coastguard Worker buffer_ = I420Buffer::Create(width_, height_);
296*d9f75844SAndroid Build Coastguard Worker memset(buffer_->MutableDataY(), 127, height_ * buffer_->StrideY());
297*d9f75844SAndroid Build Coastguard Worker memset(buffer_->MutableDataU(), 127,
298*d9f75844SAndroid Build Coastguard Worker buffer_->ChromaHeight() * buffer_->StrideU());
299*d9f75844SAndroid Build Coastguard Worker memset(buffer_->MutableDataV(), 127,
300*d9f75844SAndroid Build Coastguard Worker buffer_->ChromaHeight() * buffer_->StrideV());
301*d9f75844SAndroid Build Coastguard Worker
302*d9f75844SAndroid Build Coastguard Worker for (int i = 0; i < kSquareNum; ++i) {
303*d9f75844SAndroid Build Coastguard Worker int length = random_generator_.Rand(1, width_ > 4 ? width_ / 4 : 1);
304*d9f75844SAndroid Build Coastguard Worker // Limit the length of later squares so that they don't overwrite the
305*d9f75844SAndroid Build Coastguard Worker // previous ones too much.
306*d9f75844SAndroid Build Coastguard Worker length = (length * (kSquareNum - i)) / kSquareNum;
307*d9f75844SAndroid Build Coastguard Worker
308*d9f75844SAndroid Build Coastguard Worker int x = random_generator_.Rand(0, width_ - length);
309*d9f75844SAndroid Build Coastguard Worker int y = random_generator_.Rand(0, height_ - length);
310*d9f75844SAndroid Build Coastguard Worker uint8_t yuv_y = random_generator_.Rand(0, 255);
311*d9f75844SAndroid Build Coastguard Worker uint8_t yuv_u = random_generator_.Rand(0, 255);
312*d9f75844SAndroid Build Coastguard Worker uint8_t yuv_v = random_generator_.Rand(0, 255);
313*d9f75844SAndroid Build Coastguard Worker
314*d9f75844SAndroid Build Coastguard Worker for (int yy = y; yy < y + length; ++yy) {
315*d9f75844SAndroid Build Coastguard Worker uint8_t* pos_y = (buffer_->MutableDataY() + x + yy * buffer_->StrideY());
316*d9f75844SAndroid Build Coastguard Worker memset(pos_y, yuv_y, length);
317*d9f75844SAndroid Build Coastguard Worker }
318*d9f75844SAndroid Build Coastguard Worker for (int yy = y; yy < y + length; yy += 2) {
319*d9f75844SAndroid Build Coastguard Worker uint8_t* pos_u =
320*d9f75844SAndroid Build Coastguard Worker (buffer_->MutableDataU() + x / 2 + yy / 2 * buffer_->StrideU());
321*d9f75844SAndroid Build Coastguard Worker memset(pos_u, yuv_u, length / 2);
322*d9f75844SAndroid Build Coastguard Worker uint8_t* pos_v =
323*d9f75844SAndroid Build Coastguard Worker (buffer_->MutableDataV() + x / 2 + yy / 2 * buffer_->StrideV());
324*d9f75844SAndroid Build Coastguard Worker memset(pos_v, yuv_v, length / 2);
325*d9f75844SAndroid Build Coastguard Worker }
326*d9f75844SAndroid Build Coastguard Worker }
327*d9f75844SAndroid Build Coastguard Worker }
328*d9f75844SAndroid Build Coastguard Worker
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)329*d9f75844SAndroid Build Coastguard Worker ScrollingImageFrameGenerator::ScrollingImageFrameGenerator(
330*d9f75844SAndroid Build Coastguard Worker Clock* clock,
331*d9f75844SAndroid Build Coastguard Worker const std::vector<FILE*>& files,
332*d9f75844SAndroid Build Coastguard Worker size_t source_width,
333*d9f75844SAndroid Build Coastguard Worker size_t source_height,
334*d9f75844SAndroid Build Coastguard Worker size_t target_width,
335*d9f75844SAndroid Build Coastguard Worker size_t target_height,
336*d9f75844SAndroid Build Coastguard Worker int64_t scroll_time_ms,
337*d9f75844SAndroid Build Coastguard Worker int64_t pause_time_ms)
338*d9f75844SAndroid Build Coastguard Worker : clock_(clock),
339*d9f75844SAndroid Build Coastguard Worker start_time_(clock->TimeInMilliseconds()),
340*d9f75844SAndroid Build Coastguard Worker scroll_time_(scroll_time_ms),
341*d9f75844SAndroid Build Coastguard Worker pause_time_(pause_time_ms),
342*d9f75844SAndroid Build Coastguard Worker num_frames_(files.size()),
343*d9f75844SAndroid Build Coastguard Worker target_width_(static_cast<int>(target_width)),
344*d9f75844SAndroid Build Coastguard Worker target_height_(static_cast<int>(target_height)),
345*d9f75844SAndroid Build Coastguard Worker current_frame_num_(num_frames_ - 1),
346*d9f75844SAndroid Build Coastguard Worker prev_frame_not_scrolled_(false),
347*d9f75844SAndroid Build Coastguard Worker current_source_frame_(nullptr, absl::nullopt),
348*d9f75844SAndroid Build Coastguard Worker current_frame_(nullptr, absl::nullopt),
349*d9f75844SAndroid Build Coastguard Worker file_generator_(files, source_width, source_height, 1) {
350*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK(clock_ != nullptr);
351*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(num_frames_, 0);
352*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GE(source_height, target_height);
353*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GE(source_width, target_width);
354*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GE(scroll_time_ms, 0);
355*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GE(pause_time_ms, 0);
356*d9f75844SAndroid Build Coastguard Worker RTC_DCHECK_GT(scroll_time_ms + pause_time_ms, 0);
357*d9f75844SAndroid Build Coastguard Worker }
358*d9f75844SAndroid Build Coastguard Worker
359*d9f75844SAndroid Build Coastguard Worker FrameGeneratorInterface::VideoFrameData
NextFrame()360*d9f75844SAndroid Build Coastguard Worker ScrollingImageFrameGenerator::NextFrame() {
361*d9f75844SAndroid Build Coastguard Worker const int64_t kFrameDisplayTime = scroll_time_ + pause_time_;
362*d9f75844SAndroid Build Coastguard Worker const int64_t now = clock_->TimeInMilliseconds();
363*d9f75844SAndroid Build Coastguard Worker int64_t ms_since_start = now - start_time_;
364*d9f75844SAndroid Build Coastguard Worker
365*d9f75844SAndroid Build Coastguard Worker size_t frame_num = (ms_since_start / kFrameDisplayTime) % num_frames_;
366*d9f75844SAndroid Build Coastguard Worker UpdateSourceFrame(frame_num);
367*d9f75844SAndroid Build Coastguard Worker
368*d9f75844SAndroid Build Coastguard Worker bool cur_frame_not_scrolled;
369*d9f75844SAndroid Build Coastguard Worker
370*d9f75844SAndroid Build Coastguard Worker double scroll_factor;
371*d9f75844SAndroid Build Coastguard Worker int64_t time_into_frame = ms_since_start % kFrameDisplayTime;
372*d9f75844SAndroid Build Coastguard Worker if (time_into_frame < scroll_time_) {
373*d9f75844SAndroid Build Coastguard Worker scroll_factor = static_cast<double>(time_into_frame) / scroll_time_;
374*d9f75844SAndroid Build Coastguard Worker cur_frame_not_scrolled = false;
375*d9f75844SAndroid Build Coastguard Worker } else {
376*d9f75844SAndroid Build Coastguard Worker scroll_factor = 1.0;
377*d9f75844SAndroid Build Coastguard Worker cur_frame_not_scrolled = true;
378*d9f75844SAndroid Build Coastguard Worker }
379*d9f75844SAndroid Build Coastguard Worker CropSourceToScrolledImage(scroll_factor);
380*d9f75844SAndroid Build Coastguard Worker
381*d9f75844SAndroid Build Coastguard Worker bool same_scroll_position =
382*d9f75844SAndroid Build Coastguard Worker prev_frame_not_scrolled_ && cur_frame_not_scrolled;
383*d9f75844SAndroid Build Coastguard Worker if (!same_scroll_position) {
384*d9f75844SAndroid Build Coastguard Worker // If scrolling is not finished yet, force full frame update.
385*d9f75844SAndroid Build Coastguard Worker current_frame_.update_rect =
386*d9f75844SAndroid Build Coastguard Worker VideoFrame::UpdateRect{0, 0, target_width_, target_height_};
387*d9f75844SAndroid Build Coastguard Worker }
388*d9f75844SAndroid Build Coastguard Worker prev_frame_not_scrolled_ = cur_frame_not_scrolled;
389*d9f75844SAndroid Build Coastguard Worker
390*d9f75844SAndroid Build Coastguard Worker return current_frame_;
391*d9f75844SAndroid Build Coastguard Worker }
392*d9f75844SAndroid Build Coastguard Worker
UpdateSourceFrame(size_t frame_num)393*d9f75844SAndroid Build Coastguard Worker void ScrollingImageFrameGenerator::UpdateSourceFrame(size_t frame_num) {
394*d9f75844SAndroid Build Coastguard Worker VideoFrame::UpdateRect acc_update{0, 0, 0, 0};
395*d9f75844SAndroid Build Coastguard Worker while (current_frame_num_ != frame_num) {
396*d9f75844SAndroid Build Coastguard Worker current_source_frame_ = file_generator_.NextFrame();
397*d9f75844SAndroid Build Coastguard Worker if (current_source_frame_.update_rect) {
398*d9f75844SAndroid Build Coastguard Worker acc_update.Union(*current_source_frame_.update_rect);
399*d9f75844SAndroid Build Coastguard Worker }
400*d9f75844SAndroid Build Coastguard Worker current_frame_num_ = (current_frame_num_ + 1) % num_frames_;
401*d9f75844SAndroid Build Coastguard Worker }
402*d9f75844SAndroid Build Coastguard Worker current_source_frame_.update_rect = acc_update;
403*d9f75844SAndroid Build Coastguard Worker }
404*d9f75844SAndroid Build Coastguard Worker
CropSourceToScrolledImage(double scroll_factor)405*d9f75844SAndroid Build Coastguard Worker void ScrollingImageFrameGenerator::CropSourceToScrolledImage(
406*d9f75844SAndroid Build Coastguard Worker double scroll_factor) {
407*d9f75844SAndroid Build Coastguard Worker int scroll_margin_x = current_source_frame_.buffer->width() - target_width_;
408*d9f75844SAndroid Build Coastguard Worker int pixels_scrolled_x =
409*d9f75844SAndroid Build Coastguard Worker static_cast<int>(scroll_margin_x * scroll_factor + 0.5);
410*d9f75844SAndroid Build Coastguard Worker int scroll_margin_y = current_source_frame_.buffer->height() - target_height_;
411*d9f75844SAndroid Build Coastguard Worker int pixels_scrolled_y =
412*d9f75844SAndroid Build Coastguard Worker static_cast<int>(scroll_margin_y * scroll_factor + 0.5);
413*d9f75844SAndroid Build Coastguard Worker
414*d9f75844SAndroid Build Coastguard Worker rtc::scoped_refptr<I420BufferInterface> i420_buffer =
415*d9f75844SAndroid Build Coastguard Worker current_source_frame_.buffer->ToI420();
416*d9f75844SAndroid Build Coastguard Worker int offset_y =
417*d9f75844SAndroid Build Coastguard Worker (i420_buffer->StrideY() * pixels_scrolled_y) + pixels_scrolled_x;
418*d9f75844SAndroid Build Coastguard Worker int offset_u = (i420_buffer->StrideU() * (pixels_scrolled_y / 2)) +
419*d9f75844SAndroid Build Coastguard Worker (pixels_scrolled_x / 2);
420*d9f75844SAndroid Build Coastguard Worker int offset_v = (i420_buffer->StrideV() * (pixels_scrolled_y / 2)) +
421*d9f75844SAndroid Build Coastguard Worker (pixels_scrolled_x / 2);
422*d9f75844SAndroid Build Coastguard Worker
423*d9f75844SAndroid Build Coastguard Worker VideoFrame::UpdateRect update_rect =
424*d9f75844SAndroid Build Coastguard Worker current_source_frame_.update_rect->IsEmpty()
425*d9f75844SAndroid Build Coastguard Worker ? VideoFrame::UpdateRect{0, 0, 0, 0}
426*d9f75844SAndroid Build Coastguard Worker : VideoFrame::UpdateRect{0, 0, target_width_, target_height_};
427*d9f75844SAndroid Build Coastguard Worker current_frame_ = VideoFrameData(
428*d9f75844SAndroid Build Coastguard Worker WrapI420Buffer(target_width_, target_height_,
429*d9f75844SAndroid Build Coastguard Worker &i420_buffer->DataY()[offset_y], i420_buffer->StrideY(),
430*d9f75844SAndroid Build Coastguard Worker &i420_buffer->DataU()[offset_u], i420_buffer->StrideU(),
431*d9f75844SAndroid Build Coastguard Worker &i420_buffer->DataV()[offset_v], i420_buffer->StrideV(),
432*d9f75844SAndroid Build Coastguard Worker // To keep reference alive.
433*d9f75844SAndroid Build Coastguard Worker [i420_buffer] {}),
434*d9f75844SAndroid Build Coastguard Worker update_rect);
435*d9f75844SAndroid Build Coastguard Worker }
436*d9f75844SAndroid Build Coastguard Worker
437*d9f75844SAndroid Build Coastguard Worker } // namespace test
438*d9f75844SAndroid Build Coastguard Worker } // namespace webrtc
439