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/modules/desktop_capture/window_capturer.h"
11 #include "webrtc/modules/desktop_capture/app_capturer.h"
12 #include "webrtc/modules/desktop_capture/screen_capturer.h"
13 #include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
14 #include "webrtc/modules/desktop_capture/x11/shared_x_util.h"
15
16 #include <assert.h>
17 #include <string.h>
18 #include <X11/Xatom.h>
19 #include <X11/extensions/Xcomposite.h>
20 #include <X11/extensions/Xrender.h>
21 #include <X11/Xutil.h>
22 #include <X11/Xregion.h>
23
24 #include <algorithm>
25
26 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
27 #include "webrtc/modules/desktop_capture/desktop_frame.h"
28 #include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
29 #include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
30 #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
31 #include "webrtc/system_wrappers/interface/logging.h"
32 #include "webrtc/base/scoped_ptr.h"
33 #include "webrtc/system_wrappers/interface/scoped_refptr.h"
34
35 namespace webrtc {
36
37 namespace {
38
39 class WindowsCapturerProxy : DesktopCapturer::Callback {
40 public:
WindowsCapturerProxy()41 WindowsCapturerProxy() : window_capturer_(WindowCapturer::Create()) {
42 window_capturer_->Start(this);
43 }
~WindowsCapturerProxy()44 ~WindowsCapturerProxy() {}
SelectWindow(WindowId windowId)45 void SelectWindow(WindowId windowId) { window_capturer_->SelectWindow(windowId); }
GetFrame()46 rtc::scoped_ptr<DesktopFrame>& GetFrame() { return frame_; }
Capture(const DesktopRegion & region)47 void Capture(const DesktopRegion& region) { window_capturer_->Capture(region); }
48
49 // Callback interface
CreateSharedMemory(size_t)50 virtual SharedMemory *CreateSharedMemory(size_t) override { return NULL; }
OnCaptureCompleted(DesktopFrame * frame)51 virtual void OnCaptureCompleted(DesktopFrame *frame) override { frame_.reset(frame); }
52
53 private:
54 rtc::scoped_ptr<WindowCapturer> window_capturer_;
55 rtc::scoped_ptr<DesktopFrame> frame_;
56 };
57
58 class ScreenCapturerProxy : DesktopCapturer::Callback {
59 public:
ScreenCapturerProxy()60 ScreenCapturerProxy() : screen_capturer_(ScreenCapturer::Create()) {
61 screen_capturer_->SelectScreen(kFullDesktopScreenId);
62 screen_capturer_->Start(this);
63 }
Capture(const DesktopRegion & region)64 void Capture(const DesktopRegion& region) { screen_capturer_->Capture(region); }
GetFrame()65 rtc::scoped_ptr<DesktopFrame>& GetFrame() { return frame_; }
66
67 // Callback interface
CreateSharedMemory(size_t)68 virtual SharedMemory *CreateSharedMemory(size_t) override { return NULL; }
OnCaptureCompleted(DesktopFrame * frame)69 virtual void OnCaptureCompleted(DesktopFrame *frame) override { frame_.reset(frame); }
70 protected:
71 rtc::scoped_ptr<ScreenCapturer> screen_capturer_;
72 rtc::scoped_ptr<DesktopFrame> frame_;
73 };
74
75 class AppCapturerLinux : public AppCapturer {
76 public:
77 AppCapturerLinux(const DesktopCaptureOptions& options);
78 virtual ~AppCapturerLinux();
79
80 // AppCapturer interface.
81 virtual bool GetAppList(AppList* apps) override;
82 virtual bool SelectApp(ProcessId processId) override;
83 virtual bool BringAppToFront() override;
84
85 // DesktopCapturer interface.
86 virtual void Start(Callback* callback) override;
87 virtual void Stop() override;
88 virtual void Capture(const DesktopRegion& region) override;
89
90 protected:
GetDisplay()91 Display* GetDisplay() { return x_display_->display(); }
92 bool UpdateRegions();
93
94 void CaptureWebRTC(const DesktopRegion& region);
95 void CaptureSample(const DesktopRegion& region);
96
97 void FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame,Region rgn, uint32_t color);
98 private:
99 Callback* callback_;
100 ProcessId selected_process_;
101
102 // Sample Mode
103 ScreenCapturerProxy screen_capturer_proxy_;
104 // Mask of foreground (non-app windows in front of selected)
105 Region rgn_mask_;
106 // Region of selected windows
107 Region rgn_visual_;
108 // Mask of background (desktop, non-app windows behind selected)
109 Region rgn_background_;
110
111 // WebRtc Window mode
112 WindowsCapturerProxy window_capturer_proxy_;
113
114 scoped_refptr<SharedXDisplay> x_display_;
115 DISALLOW_COPY_AND_ASSIGN(AppCapturerLinux);
116 };
117
AppCapturerLinux(const DesktopCaptureOptions & options)118 AppCapturerLinux::AppCapturerLinux(const DesktopCaptureOptions& options)
119 : callback_(NULL),
120 selected_process_(0),
121 x_display_(options.x_display()) {
122 rgn_mask_ = XCreateRegion();
123 rgn_visual_ = XCreateRegion();
124 rgn_background_ = XCreateRegion();
125 }
126
~AppCapturerLinux()127 AppCapturerLinux::~AppCapturerLinux() {
128 if (rgn_mask_) {
129 XDestroyRegion(rgn_mask_);
130 }
131 if (rgn_visual_) {
132 XDestroyRegion(rgn_visual_);
133 }
134 if (rgn_background_) {
135 XDestroyRegion(rgn_background_);
136 }
137 }
138
139 // AppCapturer interface.
GetAppList(AppList * apps)140 bool AppCapturerLinux::GetAppList(AppList* apps) {
141 // Implemented in DesktopDeviceInfo
142 return true;
143 }
SelectApp(ProcessId processId)144 bool AppCapturerLinux::SelectApp(ProcessId processId) {
145 selected_process_ = processId;
146 return true;
147 }
BringAppToFront()148 bool AppCapturerLinux::BringAppToFront() {
149 // Not implemented yet: See Bug 1036653
150 return true;
151 }
152
153 // DesktopCapturer interface.
Start(Callback * callback)154 void AppCapturerLinux::Start(Callback* callback) {
155 assert(!callback_);
156 assert(callback);
157
158 callback_ = callback;
159 }
160
Stop()161 void AppCapturerLinux::Stop() {
162 callback_ = NULL;
163 }
164
Capture(const DesktopRegion & region)165 void AppCapturerLinux::Capture(const DesktopRegion& region) {
166 CaptureSample(region);
167 }
168
CaptureWebRTC(const DesktopRegion & region)169 void AppCapturerLinux::CaptureWebRTC(const DesktopRegion& region) {
170 XErrorTrap error_trap(GetDisplay());
171
172 int nScreenWidth = DisplayWidth(GetDisplay(), DefaultScreen(GetDisplay()));
173 int nScreenHeight = DisplayHeight(GetDisplay(), DefaultScreen(GetDisplay()));
174 rtc::scoped_ptr<DesktopFrame> frame(new BasicDesktopFrame(DesktopSize(nScreenWidth, nScreenHeight)));
175
176 WindowUtilX11 window_util_x11(x_display_);
177
178 ::Window root_window = XRootWindow(GetDisplay(), DefaultScreen(GetDisplay()));
179 ::Window parent;
180 ::Window root_return;
181 ::Window *children;
182 unsigned int num_children;
183 int status = XQueryTree(GetDisplay(), root_window, &root_return, &parent,
184 &children, &num_children);
185 if (status == 0) {
186 LOG(LS_ERROR) << "Failed to query for child windows for screen "
187 << DefaultScreen(GetDisplay());
188 return;
189 }
190
191 for (unsigned int i = 0; i < num_children; ++i) {
192 ::Window app_window = window_util_x11.GetApplicationWindow(children[i]);
193 if (!app_window) {
194 continue;
195 }
196
197 unsigned int processId = window_util_x11.GetWindowProcessID(app_window);
198 if(processId != 0 && processId == selected_process_) {
199 // capture
200 window_capturer_proxy_.SelectWindow(app_window);
201 window_capturer_proxy_.Capture(region);
202 DesktopFrame* frameWin = window_capturer_proxy_.GetFrame().get();
203 if (frameWin == NULL) {
204 continue;
205 }
206
207 XRectangle win_rect;
208 window_util_x11.GetWindowRect(app_window,win_rect, false);
209 if (win_rect.width <= 0 || win_rect.height <= 0) {
210 continue;
211 }
212
213 DesktopSize winFrameSize = frameWin->size();
214 DesktopRect target_rect = DesktopRect::MakeXYWH(win_rect.x,
215 win_rect.y,
216 winFrameSize.width(),
217 winFrameSize.height());
218
219 // bitblt into background frame
220 frame->CopyPixelsFrom(*frameWin, DesktopVector(0, 0), target_rect);
221 }
222 }
223
224 if (children) {
225 XFree(children);
226 }
227
228 // trigger event
229 if (callback_) {
230 callback_->OnCaptureCompleted(error_trap.GetLastErrorAndDisable() != 0 ?
231 NULL :
232 frame.release());
233 }
234 }
235
CaptureSample(const DesktopRegion & region)236 void AppCapturerLinux::CaptureSample(const DesktopRegion& region) {
237 XErrorTrap error_trap(GetDisplay());
238
239 //Capture screen >> set root window as capture window
240 screen_capturer_proxy_.Capture(region);
241 DesktopFrame* frame = screen_capturer_proxy_.GetFrame().get();
242 if (frame) {
243 // calculate app visual/foreground region
244 UpdateRegions();
245
246 // TODO: background/foreground mask colors should be configurable; see Bug 1054503
247 // fill background with black
248 FillDesktopFrameRegionWithColor(frame, rgn_background_, 0xFF000000);
249
250 // fill foreground with yellow
251 FillDesktopFrameRegionWithColor(frame, rgn_mask_, 0xFFFFFF00);
252 }
253
254 // trigger event
255 if (callback_) {
256 callback_->OnCaptureCompleted(error_trap.GetLastErrorAndDisable() != 0 ?
257 NULL :
258 screen_capturer_proxy_.GetFrame().release());
259 }
260 }
261
FillDesktopFrameRegionWithColor(DesktopFrame * pDesktopFrame,Region rgn,uint32_t color)262 void AppCapturerLinux::FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame, Region rgn, uint32_t color) {
263 XErrorTrap error_trap(GetDisplay());
264
265 if (!pDesktopFrame) {
266 return;
267 }
268 if (XEmptyRegion(rgn)) {
269 return;
270 }
271
272 REGION * st_rgn = (REGION *)rgn;
273 if(st_rgn && st_rgn->numRects > 0) {
274 for (short i = 0; i < st_rgn->numRects; i++) {
275 for (short j = st_rgn->rects[i].y1; j < st_rgn->rects[i].y2; j++) {
276 uint32_t* dst_pos = reinterpret_cast<uint32_t*>(pDesktopFrame->data() + pDesktopFrame->stride() * j);
277 for (short k = st_rgn->rects[i].x1; k < st_rgn->rects[i].x2; k++) {
278 dst_pos[k] = color;
279 }
280 }
281 }
282 }
283 }
284
UpdateRegions()285 bool AppCapturerLinux::UpdateRegions() {
286 XErrorTrap error_trap(GetDisplay());
287
288 XSubtractRegion(rgn_visual_, rgn_visual_, rgn_visual_);
289 XSubtractRegion(rgn_mask_, rgn_mask_, rgn_mask_);
290 WindowUtilX11 window_util_x11(x_display_);
291 int num_screens = XScreenCount(GetDisplay());
292 for (int screen = 0; screen < num_screens; ++screen) {
293 int nScreenCX = DisplayWidth(GetDisplay(), screen);
294 int nScreenCY = DisplayHeight(GetDisplay(), screen);
295
296 XRectangle screen_rect;
297 screen_rect.x = 0;
298 screen_rect.y = 0;
299 screen_rect.width = nScreenCX;
300 screen_rect.height = nScreenCY;
301
302 XUnionRectWithRegion(&screen_rect, rgn_background_, rgn_background_);
303 XXorRegion(rgn_mask_, rgn_mask_, rgn_mask_);
304 XXorRegion(rgn_visual_, rgn_visual_, rgn_visual_);
305
306 ::Window root_window = XRootWindow(GetDisplay(), screen);
307 ::Window parent;
308 ::Window root_return;
309 ::Window *children;
310 unsigned int num_children;
311 int status = XQueryTree(GetDisplay(), root_window, &root_return, &parent, &children, &num_children);
312 if (status == 0) {
313 LOG(LS_ERROR) << "Failed to query for child windows for screen " << screen;
314 continue;
315 }
316 for (unsigned int i = 0; i < num_children; ++i) {
317 ::Window app_window = window_util_x11.GetApplicationWindow(children[i]);
318 if (!app_window) {
319 continue;
320 }
321
322 // Get window region
323 XRectangle win_rect;
324 window_util_x11.GetWindowRect(app_window, win_rect, true);
325 if (win_rect.width <= 0 || win_rect.height <= 0) {
326 continue;
327 }
328
329 Region win_rgn = XCreateRegion();
330 XUnionRectWithRegion(&win_rect, win_rgn, win_rgn);
331 // update rgn_visual_ , rgn_mask_,
332 unsigned int processId = window_util_x11.GetWindowProcessID(app_window);
333 if (processId != 0 && processId == selected_process_) {
334 XUnionRegion(rgn_visual_, win_rgn, rgn_visual_);
335 XSubtractRegion(rgn_mask_, win_rgn, rgn_mask_);
336 } else {
337 Region win_rgn_intersect = XCreateRegion();
338 XIntersectRegion(rgn_visual_, win_rgn, win_rgn_intersect);
339
340 XSubtractRegion(rgn_visual_, win_rgn_intersect, rgn_visual_);
341 XUnionRegion(win_rgn_intersect, rgn_mask_, rgn_mask_);
342
343 if (win_rgn_intersect) {
344 XDestroyRegion(win_rgn_intersect);
345 }
346 }
347 if (win_rgn) {
348 XDestroyRegion(win_rgn);
349 }
350 }
351
352 if (children) {
353 XFree(children);
354 }
355 }
356
357 XSubtractRegion(rgn_background_, rgn_visual_, rgn_background_);
358
359 return true;
360 }
361
362 } // namespace
363
364 // static
Create(const DesktopCaptureOptions & options)365 AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) {
366 return new AppCapturerLinux(options);
367 }
368
369 } // namespace webrtc
370