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