Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / modules / desktop_capture / screen_capturer_x11.cc
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
11 #include "webrtc/modules/desktop_capture/screen_capturer.h"
12
13 #include <string.h>
14 #include <set>
15
16 #include <X11/extensions/Xdamage.h>
17 #include <X11/extensions/Xfixes.h>
18 #include <X11/Xlib.h>
19 #include <X11/Xutil.h>
20
21 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
22 #include "webrtc/modules/desktop_capture/desktop_frame.h"
23 #include "webrtc/modules/desktop_capture/differ.h"
24 #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
25 #include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
26 #include "webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.h"
27 #include "webrtc/system_wrappers/interface/logging.h"
28 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
29 #include "webrtc/system_wrappers/interface/tick_util.h"
30
31 // TODO(sergeyu): Move this to a header where it can be shared.
32 #if defined(NDEBUG)
33 #define DCHECK(condition) (void)(condition)
34 #else  // NDEBUG
35 #define DCHECK(condition) if (!(condition)) {abort();}
36 #endif
37
38 namespace webrtc {
39
40 namespace {
41
42 // A class to perform video frame capturing for Linux.
43 class ScreenCapturerLinux : public ScreenCapturer,
44                             public SharedXDisplay::XEventHandler {
45  public:
46   ScreenCapturerLinux();
47   virtual ~ScreenCapturerLinux();
48
49   // TODO(ajwong): Do we really want this to be synchronous?
50   bool Init(const DesktopCaptureOptions& options);
51
52   // DesktopCapturer interface.
53   virtual void Start(Callback* delegate) OVERRIDE;
54   virtual void Capture(const DesktopRegion& region) OVERRIDE;
55
56   // ScreenCapturer interface.
57   virtual bool GetScreenList(ScreenList* screens) OVERRIDE;
58   virtual bool SelectScreen(ScreenId id) OVERRIDE;
59
60  private:
61   Display* display() { return options_.x_display()->display(); }
62
63   // SharedXDisplay::XEventHandler interface.
64   virtual bool HandleXEvent(const XEvent& event) OVERRIDE;
65
66   void InitXDamage();
67
68   // Capture screen pixels to the current buffer in the queue. In the DAMAGE
69   // case, the ScreenCapturerHelper already holds the list of invalid rectangles
70   // from HandleXEvent(). In the non-DAMAGE case, this captures the
71   // whole screen, then calculates some invalid rectangles that include any
72   // differences between this and the previous capture.
73   DesktopFrame* CaptureScreen();
74
75   // Called when the screen configuration is changed.
76   void ScreenConfigurationChanged();
77
78   // Synchronize the current buffer with |last_buffer_|, by copying pixels from
79   // the area of |last_invalid_rects|.
80   // Note this only works on the assumption that kNumBuffers == 2, as
81   // |last_invalid_rects| holds the differences from the previous buffer and
82   // the one prior to that (which will then be the current buffer).
83   void SynchronizeFrame();
84
85   void DeinitXlib();
86
87   DesktopCaptureOptions options_;
88
89   Callback* callback_;
90
91   // X11 graphics context.
92   GC gc_;
93   Window root_window_;
94
95   // XFixes.
96   bool has_xfixes_;
97   int xfixes_event_base_;
98   int xfixes_error_base_;
99
100   // XDamage information.
101   bool use_damage_;
102   Damage damage_handle_;
103   int damage_event_base_;
104   int damage_error_base_;
105   XserverRegion damage_region_;
106
107   // Access to the X Server's pixel buffer.
108   XServerPixelBuffer x_server_pixel_buffer_;
109
110   // A thread-safe list of invalid rectangles, and the size of the most
111   // recently captured screen.
112   ScreenCapturerHelper helper_;
113
114   // Queue of the frames buffers.
115   ScreenCaptureFrameQueue queue_;
116
117   // Invalid region from the previous capture. This is used to synchronize the
118   // current with the last buffer used.
119   DesktopRegion last_invalid_region_;
120
121   // |Differ| for use when polling for changes.
122   scoped_ptr<Differ> differ_;
123
124   DISALLOW_COPY_AND_ASSIGN(ScreenCapturerLinux);
125 };
126
127 ScreenCapturerLinux::ScreenCapturerLinux()
128     : callback_(NULL),
129       gc_(NULL),
130       root_window_(BadValue),
131       has_xfixes_(false),
132       xfixes_event_base_(-1),
133       xfixes_error_base_(-1),
134       use_damage_(false),
135       damage_handle_(0),
136       damage_event_base_(-1),
137       damage_error_base_(-1),
138       damage_region_(0) {
139   helper_.SetLogGridSize(4);
140 }
141
142 ScreenCapturerLinux::~ScreenCapturerLinux() {
143   options_.x_display()->RemoveEventHandler(ConfigureNotify, this);
144   if (use_damage_) {
145     options_.x_display()->RemoveEventHandler(
146         damage_event_base_ + XDamageNotify, this);
147   }
148   DeinitXlib();
149 }
150
151 bool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) {
152   options_ = options;
153
154   root_window_ = RootWindow(display(), DefaultScreen(display()));
155   if (root_window_ == BadValue) {
156     LOG(LS_ERROR) << "Unable to get the root window";
157     DeinitXlib();
158     return false;
159   }
160
161   gc_ = XCreateGC(display(), root_window_, 0, NULL);
162   if (gc_ == NULL) {
163     LOG(LS_ERROR) << "Unable to get graphics context";
164     DeinitXlib();
165     return false;
166   }
167
168   options_.x_display()->AddEventHandler(ConfigureNotify, this);
169
170   // Check for XFixes extension. This is required for cursor shape
171   // notifications, and for our use of XDamage.
172   if (XFixesQueryExtension(display(), &xfixes_event_base_,
173                            &xfixes_error_base_)) {
174     has_xfixes_ = true;
175   } else {
176     LOG(LS_INFO) << "X server does not support XFixes.";
177   }
178
179   // Register for changes to the dimensions of the root window.
180   XSelectInput(display(), root_window_, StructureNotifyMask);
181
182   if (!x_server_pixel_buffer_.Init(display(), DefaultRootWindow(display()))) {
183     LOG(LS_ERROR) << "Failed to initialize pixel buffer.";
184     return false;
185   }
186
187   if (options_.use_update_notifications()) {
188     InitXDamage();
189   }
190
191   return true;
192 }
193
194 void ScreenCapturerLinux::InitXDamage() {
195   // Our use of XDamage requires XFixes.
196   if (!has_xfixes_) {
197     return;
198   }
199
200   // Check for XDamage extension.
201   if (!XDamageQueryExtension(display(), &damage_event_base_,
202                              &damage_error_base_)) {
203     LOG(LS_INFO) << "X server does not support XDamage.";
204     return;
205   }
206
207   // TODO(lambroslambrou): Disable DAMAGE in situations where it is known
208   // to fail, such as when Desktop Effects are enabled, with graphics
209   // drivers (nVidia, ATI) that fail to report DAMAGE notifications
210   // properly.
211
212   // Request notifications every time the screen becomes damaged.
213   damage_handle_ = XDamageCreate(display(), root_window_,
214                                  XDamageReportNonEmpty);
215   if (!damage_handle_) {
216     LOG(LS_ERROR) << "Unable to initialize XDamage.";
217     return;
218   }
219
220   // Create an XFixes server-side region to collate damage into.
221   damage_region_ = XFixesCreateRegion(display(), 0, 0);
222   if (!damage_region_) {
223     XDamageDestroy(display(), damage_handle_);
224     LOG(LS_ERROR) << "Unable to create XFixes region.";
225     return;
226   }
227
228   options_.x_display()->AddEventHandler(
229       damage_event_base_ + XDamageNotify, this);
230
231   use_damage_ = true;
232   LOG(LS_INFO) << "Using XDamage extension.";
233 }
234
235 void ScreenCapturerLinux::Start(Callback* callback) {
236   DCHECK(!callback_);
237   DCHECK(callback);
238
239   callback_ = callback;
240 }
241
242 void ScreenCapturerLinux::Capture(const DesktopRegion& region) {
243   TickTime capture_start_time = TickTime::Now();
244
245   queue_.MoveToNextFrame();
246
247   // Process XEvents for XDamage and cursor shape tracking.
248   options_.x_display()->ProcessPendingXEvents();
249
250   // ProcessPendingXEvents() may call ScreenConfigurationChanged() which
251   // reinitializes |x_server_pixel_buffer_|. Check if the pixel buffer is still
252   // in a good shape.
253   if (!x_server_pixel_buffer_.is_initialized()) {
254      // We failed to initialize pixel buffer.
255      callback_->OnCaptureCompleted(NULL);
256      return;
257   }
258
259   // If the current frame is from an older generation then allocate a new one.
260   // Note that we can't reallocate other buffers at this point, since the caller
261   // may still be reading from them.
262   if (!queue_.current_frame()) {
263     scoped_ptr<DesktopFrame> frame(
264         new BasicDesktopFrame(x_server_pixel_buffer_.window_size()));
265     queue_.ReplaceCurrentFrame(frame.release());
266   }
267
268   // Refresh the Differ helper used by CaptureFrame(), if needed.
269   DesktopFrame* frame = queue_.current_frame();
270   if (!use_damage_ && (
271       !differ_.get() ||
272       (differ_->width() != frame->size().width()) ||
273       (differ_->height() != frame->size().height()) ||
274       (differ_->bytes_per_row() != frame->stride()))) {
275     differ_.reset(new Differ(frame->size().width(), frame->size().height(),
276                              DesktopFrame::kBytesPerPixel,
277                              frame->stride()));
278   }
279
280   DesktopFrame* result = CaptureScreen();
281   last_invalid_region_ = result->updated_region();
282   result->set_capture_time_ms(
283       (TickTime::Now() - capture_start_time).Milliseconds());
284   callback_->OnCaptureCompleted(result);
285 }
286
287 bool ScreenCapturerLinux::GetScreenList(ScreenList* screens) {
288   DCHECK(screens->size() == 0);
289   // TODO(jiayl): implement screen enumeration.
290   Screen default_screen;
291   default_screen.id = 0;
292   screens->push_back(default_screen);
293   return true;
294 }
295
296 bool ScreenCapturerLinux::SelectScreen(ScreenId id) {
297   // TODO(jiayl): implement screen selection.
298   return true;
299 }
300
301 bool ScreenCapturerLinux::HandleXEvent(const XEvent& event) {
302   if (use_damage_ && (event.type == damage_event_base_ + XDamageNotify)) {
303     const XDamageNotifyEvent* damage_event =
304         reinterpret_cast<const XDamageNotifyEvent*>(&event);
305     if (damage_event->damage != damage_handle_)
306       return false;
307     DCHECK(damage_event->level == XDamageReportNonEmpty);
308     return true;
309   } else if (event.type == ConfigureNotify) {
310     ScreenConfigurationChanged();
311     return true;
312   }
313   return false;
314 }
315
316 DesktopFrame* ScreenCapturerLinux::CaptureScreen() {
317   DesktopFrame* frame = queue_.current_frame()->Share();
318   assert(x_server_pixel_buffer_.window_size().equals(frame->size()));
319
320   // Pass the screen size to the helper, so it can clip the invalid region if it
321   // expands that region to a grid.
322   helper_.set_size_most_recent(frame->size());
323
324   // In the DAMAGE case, ensure the frame is up-to-date with the previous frame
325   // if any.  If there isn't a previous frame, that means a screen-resolution
326   // change occurred, and |invalid_rects| will be updated to include the whole
327   // screen.
328   if (use_damage_ && queue_.previous_frame())
329     SynchronizeFrame();
330
331   DesktopRegion* updated_region = frame->mutable_updated_region();
332
333   x_server_pixel_buffer_.Synchronize();
334   if (use_damage_ && queue_.previous_frame()) {
335     // Atomically fetch and clear the damage region.
336     XDamageSubtract(display(), damage_handle_, None, damage_region_);
337     int rects_num = 0;
338     XRectangle bounds;
339     XRectangle* rects = XFixesFetchRegionAndBounds(display(), damage_region_,
340                                                    &rects_num, &bounds);
341     for (int i = 0; i < rects_num; ++i) {
342       updated_region->AddRect(DesktopRect::MakeXYWH(
343           rects[i].x, rects[i].y, rects[i].width, rects[i].height));
344     }
345     XFree(rects);
346     helper_.InvalidateRegion(*updated_region);
347
348     // Capture the damaged portions of the desktop.
349     helper_.TakeInvalidRegion(updated_region);
350
351     // Clip the damaged portions to the current screen size, just in case some
352     // spurious XDamage notifications were received for a previous (larger)
353     // screen size.
354     updated_region->IntersectWith(
355         DesktopRect::MakeSize(x_server_pixel_buffer_.window_size()));
356
357     for (DesktopRegion::Iterator it(*updated_region);
358          !it.IsAtEnd(); it.Advance()) {
359       x_server_pixel_buffer_.CaptureRect(it.rect(), frame);
360     }
361   } else {
362     // Doing full-screen polling, or this is the first capture after a
363     // screen-resolution change.  In either case, need a full-screen capture.
364     DesktopRect screen_rect = DesktopRect::MakeSize(frame->size());
365     x_server_pixel_buffer_.CaptureRect(screen_rect, frame);
366
367     if (queue_.previous_frame()) {
368       // Full-screen polling, so calculate the invalid rects here, based on the
369       // changed pixels between current and previous buffers.
370       DCHECK(differ_.get() != NULL);
371       DCHECK(queue_.previous_frame()->data());
372       differ_->CalcDirtyRegion(queue_.previous_frame()->data(),
373                                frame->data(), updated_region);
374     } else {
375       // No previous buffer, so always invalidate the whole screen, whether
376       // or not DAMAGE is being used.  DAMAGE doesn't necessarily send a
377       // full-screen notification after a screen-resolution change, so
378       // this is done here.
379       updated_region->SetRect(screen_rect);
380     }
381   }
382
383   return frame;
384 }
385
386 void ScreenCapturerLinux::ScreenConfigurationChanged() {
387   // Make sure the frame buffers will be reallocated.
388   queue_.Reset();
389
390   helper_.ClearInvalidRegion();
391   if (!x_server_pixel_buffer_.Init(display(), DefaultRootWindow(display()))) {
392     LOG(LS_ERROR) << "Failed to initialize pixel buffer after screen "
393         "configuration change.";
394   }
395 }
396
397 void ScreenCapturerLinux::SynchronizeFrame() {
398   // Synchronize the current buffer with the previous one since we do not
399   // capture the entire desktop. Note that encoder may be reading from the
400   // previous buffer at this time so thread access complaints are false
401   // positives.
402
403   // TODO(hclam): We can reduce the amount of copying here by subtracting
404   // |capturer_helper_|s region from |last_invalid_region_|.
405   // http://crbug.com/92354
406   DCHECK(queue_.previous_frame());
407
408   DesktopFrame* current = queue_.current_frame();
409   DesktopFrame* last = queue_.previous_frame();
410   DCHECK(current != last);
411   for (DesktopRegion::Iterator it(last_invalid_region_);
412        !it.IsAtEnd(); it.Advance()) {
413     current->CopyPixelsFrom(*last, it.rect().top_left(), it.rect());
414   }
415 }
416
417 void ScreenCapturerLinux::DeinitXlib() {
418   if (gc_) {
419     XFreeGC(display(), gc_);
420     gc_ = NULL;
421   }
422
423   x_server_pixel_buffer_.Release();
424
425   if (display()) {
426     if (damage_handle_) {
427       XDamageDestroy(display(), damage_handle_);
428       damage_handle_ = 0;
429     }
430
431     if (damage_region_) {
432       XFixesDestroyRegion(display(), damage_region_);
433       damage_region_ = 0;
434     }
435   }
436 }
437
438 }  // namespace
439
440 // static
441 ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
442   if (!options.x_display())
443     return NULL;
444
445   scoped_ptr<ScreenCapturerLinux> capturer(new ScreenCapturerLinux());
446   if (!capturer->Init(options))
447     capturer.reset();
448   return capturer.release();
449 }
450
451 }  // namespace webrtc