1 /*
2  *  Copyright (c) 2016 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 "modules/desktop_capture/desktop_frame_generator.h"
12 
13 #include <stdint.h>
14 #include <string.h>
15 
16 #include <memory>
17 
18 #include "modules/desktop_capture/rgba_color.h"
19 #include "rtc_base/random.h"
20 #include "rtc_base/timeutils.h"
21 
22 namespace webrtc {
23 
24 namespace {
25 
26 // Sets |updated_region| to |frame|. If |enlarge_updated_region| is
27 // true, this function will randomly enlarge each DesktopRect in
28 // |updated_region|. But the enlarged DesktopRegion won't excceed the
29 // frame->size(). If |add_random_updated_region| is true, several random
30 // rectangles will also be included in |frame|.
SetUpdatedRegion(DesktopFrame * frame,const DesktopRegion & updated_region,bool enlarge_updated_region,int enlarge_range,bool add_random_updated_region)31 void SetUpdatedRegion(DesktopFrame* frame,
32                       const DesktopRegion& updated_region,
33                       bool enlarge_updated_region,
34                       int enlarge_range,
35                       bool add_random_updated_region) {
36   const DesktopRect screen_rect = DesktopRect::MakeSize(frame->size());
37   Random random(rtc::TimeMicros());
38   frame->mutable_updated_region()->Clear();
39   for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
40        it.Advance()) {
41     DesktopRect rect = it.rect();
42     if (enlarge_updated_region && enlarge_range > 0) {
43       rect.Extend(random.Rand(enlarge_range), random.Rand(enlarge_range),
44                   random.Rand(enlarge_range), random.Rand(enlarge_range));
45       rect.IntersectWith(screen_rect);
46     }
47     frame->mutable_updated_region()->AddRect(rect);
48   }
49 
50   if (add_random_updated_region) {
51     for (int i = random.Rand(10); i >= 0; i--) {
52       // At least a 1 x 1 updated region.
53       const int left = random.Rand(0, frame->size().width() - 2);
54       const int top = random.Rand(0, frame->size().height() - 2);
55       const int right = random.Rand(left + 1, frame->size().width());
56       const int bottom = random.Rand(top + 1, frame->size().height());
57       frame->mutable_updated_region()->AddRect(
58           DesktopRect::MakeLTRB(left, top, right, bottom));
59     }
60   }
61 }
62 
63 // Paints pixels in |rect| of |frame| to |color|.
PaintRect(DesktopFrame * frame,DesktopRect rect,RgbaColor rgba_color)64 void PaintRect(DesktopFrame* frame, DesktopRect rect, RgbaColor rgba_color) {
65   static_assert(DesktopFrame::kBytesPerPixel == sizeof(uint32_t),
66                 "kBytesPerPixel should be 4.");
67   RTC_DCHECK_GE(frame->size().width(), rect.right());
68   RTC_DCHECK_GE(frame->size().height(), rect.bottom());
69   uint32_t color = rgba_color.ToUInt32();
70   uint8_t* row = frame->GetFrameDataAtPos(rect.top_left());
71   for (int i = 0; i < rect.height(); i++) {
72     uint32_t* column = reinterpret_cast<uint32_t*>(row);
73     for (int j = 0; j < rect.width(); j++) {
74       column[j] = color;
75     }
76     row += frame->stride();
77   }
78 }
79 
80 // Paints pixels in |region| of |frame| to |color|.
PaintRegion(DesktopFrame * frame,DesktopRegion * region,RgbaColor rgba_color)81 void PaintRegion(DesktopFrame* frame,
82                  DesktopRegion* region,
83                  RgbaColor rgba_color) {
84   region->IntersectWith(DesktopRect::MakeSize(frame->size()));
85   for (DesktopRegion::Iterator it(*region); !it.IsAtEnd(); it.Advance()) {
86     PaintRect(frame, it.rect(), rgba_color);
87   }
88 }
89 
90 }  // namespace
91 
DesktopFrameGenerator()92 DesktopFrameGenerator::DesktopFrameGenerator() {}
~DesktopFrameGenerator()93 DesktopFrameGenerator::~DesktopFrameGenerator() {}
94 
DesktopFramePainter()95 DesktopFramePainter::DesktopFramePainter() {}
~DesktopFramePainter()96 DesktopFramePainter::~DesktopFramePainter() {}
97 
PainterDesktopFrameGenerator()98 PainterDesktopFrameGenerator::PainterDesktopFrameGenerator()
99     : size_(1024, 768),
100       return_frame_(true),
101       provide_updated_region_hints_(false),
102       enlarge_updated_region_(false),
103       enlarge_range_(20),
104       add_random_updated_region_(false),
105       painter_(nullptr) {}
~PainterDesktopFrameGenerator()106 PainterDesktopFrameGenerator::~PainterDesktopFrameGenerator() {}
107 
GetNextFrame(SharedMemoryFactory * factory)108 std::unique_ptr<DesktopFrame> PainterDesktopFrameGenerator::GetNextFrame(
109     SharedMemoryFactory* factory) {
110   if (!return_frame_) {
111     return nullptr;
112   }
113 
114   std::unique_ptr<DesktopFrame> frame = std::unique_ptr<DesktopFrame>(
115       factory ? SharedMemoryDesktopFrame::Create(size_, factory).release()
116               : new BasicDesktopFrame(size_));
117   if (painter_) {
118     DesktopRegion updated_region;
119     if (!painter_->Paint(frame.get(), &updated_region)) {
120       return nullptr;
121     }
122 
123     if (provide_updated_region_hints_) {
124       SetUpdatedRegion(frame.get(), updated_region, enlarge_updated_region_,
125                        enlarge_range_, add_random_updated_region_);
126     } else {
127       frame->mutable_updated_region()->SetRect(
128           DesktopRect::MakeSize(frame->size()));
129     }
130   }
131 
132   return frame;
133 }
134 
size()135 DesktopSize* PainterDesktopFrameGenerator::size() {
136   return &size_;
137 }
138 
set_return_frame(bool return_frame)139 void PainterDesktopFrameGenerator::set_return_frame(bool return_frame) {
140   return_frame_ = return_frame;
141 }
142 
set_provide_updated_region_hints(bool provide_updated_region_hints)143 void PainterDesktopFrameGenerator::set_provide_updated_region_hints(
144     bool provide_updated_region_hints) {
145   provide_updated_region_hints_ = provide_updated_region_hints;
146 }
147 
set_enlarge_updated_region(bool enlarge_updated_region)148 void PainterDesktopFrameGenerator::set_enlarge_updated_region(
149     bool enlarge_updated_region) {
150   enlarge_updated_region_ = enlarge_updated_region;
151 }
152 
set_enlarge_range(int enlarge_range)153 void PainterDesktopFrameGenerator::set_enlarge_range(int enlarge_range) {
154   enlarge_range_ = enlarge_range;
155 }
156 
set_add_random_updated_region(bool add_random_updated_region)157 void PainterDesktopFrameGenerator::set_add_random_updated_region(
158     bool add_random_updated_region) {
159   add_random_updated_region_ = add_random_updated_region;
160 }
161 
set_desktop_frame_painter(DesktopFramePainter * painter)162 void PainterDesktopFrameGenerator::set_desktop_frame_painter(
163     DesktopFramePainter* painter) {
164   painter_ = painter;
165 }
166 
BlackWhiteDesktopFramePainter()167 BlackWhiteDesktopFramePainter::BlackWhiteDesktopFramePainter() {}
~BlackWhiteDesktopFramePainter()168 BlackWhiteDesktopFramePainter::~BlackWhiteDesktopFramePainter() {}
169 
updated_region()170 DesktopRegion* BlackWhiteDesktopFramePainter::updated_region() {
171   return &updated_region_;
172 }
173 
Paint(DesktopFrame * frame,DesktopRegion * updated_region)174 bool BlackWhiteDesktopFramePainter::Paint(DesktopFrame* frame,
175                                           DesktopRegion* updated_region) {
176   RTC_DCHECK(updated_region->is_empty());
177   memset(frame->data(), 0, frame->stride() * frame->size().height());
178   PaintRegion(frame, &updated_region_, RgbaColor(0xFFFFFFFF));
179   updated_region_.Swap(updated_region);
180   return true;
181 }
182 
183 }  // namespace webrtc
184