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