Line data Source code
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/app_capturer.h"
11 : #include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
12 : #include "webrtc/modules/desktop_capture/x11/shared_x_util.h"
13 :
14 : #include <assert.h>
15 : #include <string.h>
16 : #include <X11/Xatom.h>
17 : #include <X11/extensions/Xcomposite.h>
18 : #include <X11/extensions/Xrender.h>
19 : #include <X11/Xutil.h>
20 : #include <X11/Xregion.h>
21 :
22 : #include <algorithm>
23 :
24 : #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
25 : #include "webrtc/modules/desktop_capture/desktop_frame.h"
26 : #include "webrtc/modules/desktop_capture/x11/shared_x_display.h"
27 : #include "webrtc/modules/desktop_capture/x11/x_error_trap.h"
28 : #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
29 : #include "webrtc/system_wrappers/include/logging.h"
30 :
31 : namespace webrtc {
32 :
33 : namespace {
34 :
35 0 : class ScreenCapturerProxy : DesktopCapturer::Callback {
36 : public:
37 0 : ScreenCapturerProxy()
38 0 : : screen_capturer_(DesktopCapturer::CreateScreenCapturer(DesktopCaptureOptions::CreateDefault())) {
39 0 : screen_capturer_->SelectSource(kFullDesktopScreenId);
40 0 : screen_capturer_->Start(this);
41 0 : }
42 0 : void CaptureFrame() { screen_capturer_->CaptureFrame(); }
43 0 : std::unique_ptr<DesktopFrame> GetFrame() { return std::move(frame_); }
44 :
45 : // Callback interface
46 0 : virtual void OnCaptureResult(DesktopCapturer::Result result,
47 : std::unique_ptr<DesktopFrame> frame) {
48 0 : frame_ = std::move(frame);
49 0 : }
50 :
51 : protected:
52 : std::unique_ptr<DesktopCapturer> screen_capturer_;
53 : std::unique_ptr<DesktopFrame> frame_;
54 : };
55 :
56 : class AppCapturerLinux : public AppCapturer {
57 : public:
58 : AppCapturerLinux(const DesktopCaptureOptions& options);
59 : virtual ~AppCapturerLinux();
60 :
61 : // AppCapturer interface.
62 : virtual bool GetAppList(AppList* apps) override;
63 : virtual bool SelectApp(ProcessId processId) override;
64 : virtual bool BringAppToFront() override;
65 :
66 : // DesktopCapturer interface.
67 : virtual void Start(Callback* callback) override;
68 : virtual void Stop() override;
69 : virtual void CaptureFrame() override;
70 0 : virtual bool SelectSource(SourceId id) override
71 : {
72 0 : return SelectApp(static_cast<ProcessId>(id));
73 : }
74 :
75 : protected:
76 0 : Display* GetDisplay() { return x_display_->display(); }
77 : bool UpdateRegions();
78 :
79 : void FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame,Region rgn, uint32_t color);
80 : private:
81 : Callback* callback_;
82 : ProcessId selected_process_;
83 :
84 : // Sample Mode
85 : ScreenCapturerProxy screen_capturer_proxy_;
86 : // Mask of foreground (non-app windows in front of selected)
87 : Region rgn_mask_;
88 : // Region of selected windows
89 : Region rgn_visual_;
90 : // Mask of background (desktop, non-app windows behind selected)
91 : Region rgn_background_;
92 :
93 : rtc::scoped_refptr<SharedXDisplay> x_display_;
94 : RTC_DISALLOW_COPY_AND_ASSIGN(AppCapturerLinux);
95 : };
96 :
97 0 : AppCapturerLinux::AppCapturerLinux(const DesktopCaptureOptions& options)
98 : : callback_(NULL),
99 : selected_process_(0),
100 0 : x_display_(options.x_display()) {
101 0 : rgn_mask_ = XCreateRegion();
102 0 : rgn_visual_ = XCreateRegion();
103 0 : rgn_background_ = XCreateRegion();
104 0 : }
105 :
106 0 : AppCapturerLinux::~AppCapturerLinux() {
107 0 : if (rgn_mask_) {
108 0 : XDestroyRegion(rgn_mask_);
109 : }
110 0 : if (rgn_visual_) {
111 0 : XDestroyRegion(rgn_visual_);
112 : }
113 0 : if (rgn_background_) {
114 0 : XDestroyRegion(rgn_background_);
115 : }
116 0 : }
117 :
118 : // AppCapturer interface.
119 0 : bool AppCapturerLinux::GetAppList(AppList* apps) {
120 : // Implemented in DesktopDeviceInfo
121 0 : return true;
122 : }
123 0 : bool AppCapturerLinux::SelectApp(ProcessId processId) {
124 0 : selected_process_ = processId;
125 0 : return true;
126 : }
127 0 : bool AppCapturerLinux::BringAppToFront() {
128 : // Not implemented yet: See Bug 1036653
129 0 : return true;
130 : }
131 :
132 : // DesktopCapturer interface.
133 0 : void AppCapturerLinux::Start(Callback* callback) {
134 0 : assert(!callback_);
135 0 : assert(callback);
136 :
137 0 : callback_ = callback;
138 0 : }
139 :
140 0 : void AppCapturerLinux::Stop() {
141 0 : callback_ = NULL;
142 0 : }
143 :
144 0 : void AppCapturerLinux::CaptureFrame() {
145 0 : XErrorTrap error_trap(GetDisplay());
146 :
147 : //Capture screen >> set root window as capture window
148 0 : screen_capturer_proxy_.CaptureFrame();
149 0 : std::unique_ptr<DesktopFrame> frame = std::move(screen_capturer_proxy_.GetFrame());
150 0 : if (frame) {
151 :
152 : // calculate app visual/foreground region
153 0 : UpdateRegions();
154 :
155 : // TODO: background/foreground mask colors should be configurable; see Bug 1054503
156 : // fill background with black
157 0 : FillDesktopFrameRegionWithColor(frame.get(), rgn_background_, 0xFF000000);
158 :
159 : // fill foreground with yellow
160 0 : FillDesktopFrameRegionWithColor(frame.get(), rgn_mask_, 0xFFFFFF00);
161 : }
162 :
163 : // trigger event
164 0 : if (callback_) {
165 0 : bool worked = error_trap.GetLastErrorAndDisable() == 0;
166 0 : DesktopCapturer::Result res = worked ? DesktopCapturer::Result::SUCCESS
167 0 : : DesktopCapturer::Result::ERROR_TEMPORARY;
168 0 : callback_->OnCaptureResult(res, std::move(frame));
169 : }
170 0 : }
171 :
172 0 : void AppCapturerLinux::FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame, Region rgn, uint32_t color) {
173 0 : XErrorTrap error_trap(GetDisplay());
174 :
175 0 : if (!pDesktopFrame) {
176 0 : return;
177 : }
178 0 : if (XEmptyRegion(rgn)) {
179 0 : return;
180 : }
181 :
182 0 : REGION * st_rgn = (REGION *)rgn;
183 0 : if(st_rgn && st_rgn->numRects > 0) {
184 0 : for (short i = 0; i < st_rgn->numRects; i++) {
185 0 : for (short j = st_rgn->rects[i].y1; j < st_rgn->rects[i].y2; j++) {
186 0 : uint32_t* dst_pos = reinterpret_cast<uint32_t*>(pDesktopFrame->data() + pDesktopFrame->stride() * j);
187 0 : for (short k = st_rgn->rects[i].x1; k < st_rgn->rects[i].x2; k++) {
188 0 : dst_pos[k] = color;
189 : }
190 : }
191 : }
192 : }
193 : }
194 :
195 0 : bool AppCapturerLinux::UpdateRegions() {
196 0 : XErrorTrap error_trap(GetDisplay());
197 :
198 0 : XSubtractRegion(rgn_visual_, rgn_visual_, rgn_visual_);
199 0 : XSubtractRegion(rgn_mask_, rgn_mask_, rgn_mask_);
200 0 : WindowUtilX11 window_util_x11(x_display_);
201 0 : int num_screens = XScreenCount(GetDisplay());
202 0 : for (int screen = 0; screen < num_screens; ++screen) {
203 0 : int nScreenCX = DisplayWidth(GetDisplay(), screen);
204 0 : int nScreenCY = DisplayHeight(GetDisplay(), screen);
205 :
206 : XRectangle screen_rect;
207 0 : screen_rect.x = 0;
208 0 : screen_rect.y = 0;
209 0 : screen_rect.width = nScreenCX;
210 0 : screen_rect.height = nScreenCY;
211 :
212 0 : XUnionRectWithRegion(&screen_rect, rgn_background_, rgn_background_);
213 0 : XXorRegion(rgn_mask_, rgn_mask_, rgn_mask_);
214 0 : XXorRegion(rgn_visual_, rgn_visual_, rgn_visual_);
215 :
216 0 : ::Window root_window = XRootWindow(GetDisplay(), screen);
217 : ::Window parent;
218 : ::Window root_return;
219 : ::Window *children;
220 : unsigned int num_children;
221 0 : int status = XQueryTree(GetDisplay(), root_window, &root_return, &parent, &children, &num_children);
222 0 : if (status == 0) {
223 0 : LOG(LS_ERROR) << "Failed to query for child windows for screen " << screen;
224 0 : continue;
225 : }
226 0 : for (unsigned int i = 0; i < num_children; ++i) {
227 0 : ::Window app_window = window_util_x11.GetApplicationWindow(children[i]);
228 0 : if (!app_window) {
229 0 : continue;
230 : }
231 :
232 : // Get window region
233 : XRectangle win_rect;
234 0 : window_util_x11.GetWindowRect(app_window, win_rect, true);
235 0 : if (win_rect.width <= 0 || win_rect.height <= 0) {
236 0 : continue;
237 : }
238 :
239 0 : Region win_rgn = XCreateRegion();
240 0 : XUnionRectWithRegion(&win_rect, win_rgn, win_rgn);
241 : // update rgn_visual_ , rgn_mask_,
242 0 : unsigned int processId = window_util_x11.GetWindowProcessID(app_window);
243 0 : if (processId != 0 && processId == selected_process_) {
244 0 : XUnionRegion(rgn_visual_, win_rgn, rgn_visual_);
245 0 : XSubtractRegion(rgn_mask_, win_rgn, rgn_mask_);
246 : } else {
247 0 : Region win_rgn_intersect = XCreateRegion();
248 0 : XIntersectRegion(rgn_visual_, win_rgn, win_rgn_intersect);
249 :
250 0 : XSubtractRegion(rgn_visual_, win_rgn_intersect, rgn_visual_);
251 0 : XUnionRegion(win_rgn_intersect, rgn_mask_, rgn_mask_);
252 :
253 0 : if (win_rgn_intersect) {
254 0 : XDestroyRegion(win_rgn_intersect);
255 : }
256 : }
257 0 : if (win_rgn) {
258 0 : XDestroyRegion(win_rgn);
259 : }
260 : }
261 :
262 0 : if (children) {
263 0 : XFree(children);
264 : }
265 : }
266 :
267 0 : XSubtractRegion(rgn_background_, rgn_visual_, rgn_background_);
268 :
269 0 : return true;
270 : }
271 :
272 : } // namespace
273 :
274 : // static
275 0 : AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) {
276 0 : return new AppCapturerLinux(options);
277 : }
278 :
279 : // static
280 0 : std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawAppCapturer(
281 : const DesktopCaptureOptions& options) {
282 :
283 0 : if (!options.x_display())
284 0 : return nullptr;
285 :
286 0 : std::unique_ptr<AppCapturerLinux> capturer(new AppCapturerLinux(options));
287 :
288 0 : return std::unique_ptr<DesktopCapturer>(std::move(capturer));
289 : }
290 :
291 : } // namespace webrtc
|