Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / modules / desktop_capture / screen_capturer_win.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 <windows.h>
14
15 #include "webrtc/modules/desktop_capture/desktop_capture_options.h"
16 #include "webrtc/modules/desktop_capture/desktop_frame.h"
17 #include "webrtc/modules/desktop_capture/desktop_frame_win.h"
18 #include "webrtc/modules/desktop_capture/desktop_region.h"
19 #include "webrtc/modules/desktop_capture/differ.h"
20 #include "webrtc/modules/desktop_capture/mouse_cursor.h"
21 #include "webrtc/modules/desktop_capture/mouse_cursor_shape.h"
22 #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h"
23 #include "webrtc/modules/desktop_capture/screen_capturer_helper.h"
24 #include "webrtc/modules/desktop_capture/win/cursor.h"
25 #include "webrtc/modules/desktop_capture/win/desktop.h"
26 #include "webrtc/modules/desktop_capture/win/scoped_thread_desktop.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 namespace webrtc {
32
33 namespace {
34
35 // Constants from dwmapi.h.
36 const UINT DWM_EC_DISABLECOMPOSITION = 0;
37 const UINT DWM_EC_ENABLECOMPOSITION = 1;
38
39 typedef HRESULT (WINAPI * DwmEnableCompositionFunc)(UINT);
40
41 const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll";
42
43 // ScreenCapturerWin captures 32bit RGB using GDI.
44 //
45 // ScreenCapturerWin is double-buffered as required by ScreenCapturer.
46 class ScreenCapturerWin : public ScreenCapturer {
47  public:
48   ScreenCapturerWin(const DesktopCaptureOptions& options);
49   virtual ~ScreenCapturerWin();
50
51   // Overridden from ScreenCapturer:
52   virtual void Start(Callback* callback) OVERRIDE;
53   virtual void Capture(const DesktopRegion& region) OVERRIDE;
54   virtual void SetMouseShapeObserver(
55       MouseShapeObserver* mouse_shape_observer) OVERRIDE;
56   virtual bool GetScreenList(ScreenList* screens) OVERRIDE;
57   virtual bool SelectScreen(ScreenId id) OVERRIDE;
58
59  private:
60   // Make sure that the device contexts match the screen configuration.
61   void PrepareCaptureResources();
62
63   // Captures the current screen contents into the current buffer. Returns true
64   // if succeeded.
65   bool CaptureImage();
66
67   // Capture the current cursor shape.
68   void CaptureCursor();
69
70   // Get the rect of the currently selected screen. If the screen is disabled
71   // or disconnected, or any error happens, an empty rect is returned.
72   DesktopRect GetScreenRect();
73
74   Callback* callback_;
75   MouseShapeObserver* mouse_shape_observer_;
76   ScreenId current_screen_id_;
77   std::wstring current_device_key_;
78
79   // A thread-safe list of invalid rectangles, and the size of the most
80   // recently captured screen.
81   ScreenCapturerHelper helper_;
82
83   // Snapshot of the last cursor bitmap we sent to the client. This is used
84   // to diff against the current cursor so we only send a cursor-change
85   // message when the shape has changed.
86   MouseCursorShape last_cursor_;
87
88   ScopedThreadDesktop desktop_;
89
90   // GDI resources used for screen capture.
91   HDC desktop_dc_;
92   HDC memory_dc_;
93
94   // Queue of the frames buffers.
95   ScreenCaptureFrameQueue queue_;
96
97   // Rectangle describing the bounds of the desktop device context.
98   DesktopRect desktop_dc_rect_;
99
100   // Class to calculate the difference between two screen bitmaps.
101   scoped_ptr<Differ> differ_;
102
103   HMODULE dwmapi_library_;
104   DwmEnableCompositionFunc composition_func_;
105
106   // Used to suppress duplicate logging of SetThreadExecutionState errors.
107   bool set_thread_execution_state_failed_;
108
109   DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWin);
110 };
111
112 ScreenCapturerWin::ScreenCapturerWin(const DesktopCaptureOptions& options)
113     : callback_(NULL),
114       mouse_shape_observer_(NULL),
115       current_screen_id_(kFullDesktopScreenId),
116       desktop_dc_(NULL),
117       memory_dc_(NULL),
118       dwmapi_library_(NULL),
119       composition_func_(NULL),
120       set_thread_execution_state_failed_(false) {
121   if (options.disable_effects()) {
122     // Load dwmapi.dll dynamically since it is not available on XP.
123     if (!dwmapi_library_)
124       dwmapi_library_ = LoadLibrary(kDwmapiLibraryName);
125
126     if (dwmapi_library_) {
127       composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
128           GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
129     }
130   }
131 }
132
133 ScreenCapturerWin::~ScreenCapturerWin() {
134   if (desktop_dc_)
135     ReleaseDC(NULL, desktop_dc_);
136   if (memory_dc_)
137     DeleteDC(memory_dc_);
138
139   // Restore Aero.
140   if (composition_func_)
141     (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
142
143   if (dwmapi_library_)
144     FreeLibrary(dwmapi_library_);
145 }
146
147 void ScreenCapturerWin::Capture(const DesktopRegion& region) {
148   TickTime capture_start_time = TickTime::Now();
149
150   queue_.MoveToNextFrame();
151
152   // Request that the system not power-down the system, or the display hardware.
153   if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) {
154     if (!set_thread_execution_state_failed_) {
155       set_thread_execution_state_failed_ = true;
156       LOG_F(LS_WARNING) << "Failed to make system & display power assertion: "
157                         << GetLastError();
158     }
159   }
160
161   // Make sure the GDI capture resources are up-to-date.
162   PrepareCaptureResources();
163
164   // Copy screen bits to the current buffer.
165   if (!CaptureImage()) {
166     callback_->OnCaptureCompleted(NULL);
167     return;
168   }
169
170   const DesktopFrame* current_frame = queue_.current_frame();
171   const DesktopFrame* last_frame = queue_.previous_frame();
172   if (last_frame && last_frame->size().equals(current_frame->size())) {
173     // Make sure the differencer is set up correctly for these previous and
174     // current screens.
175     if (!differ_.get() ||
176         (differ_->width() != current_frame->size().width()) ||
177         (differ_->height() != current_frame->size().height()) ||
178         (differ_->bytes_per_row() != current_frame->stride())) {
179       differ_.reset(new Differ(current_frame->size().width(),
180                                current_frame->size().height(),
181                                DesktopFrame::kBytesPerPixel,
182                                current_frame->stride()));
183     }
184
185     // Calculate difference between the two last captured frames.
186     DesktopRegion region;
187     differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(),
188                              &region);
189     helper_.InvalidateRegion(region);
190   } else {
191     // No previous frame is available. Invalidate the whole screen.
192     helper_.InvalidateScreen(current_frame->size());
193   }
194
195   helper_.set_size_most_recent(current_frame->size());
196
197   // Emit the current frame.
198   DesktopFrame* frame = queue_.current_frame()->Share();
199   frame->set_dpi(DesktopVector(
200       GetDeviceCaps(desktop_dc_, LOGPIXELSX),
201       GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
202   frame->mutable_updated_region()->Clear();
203   helper_.TakeInvalidRegion(frame->mutable_updated_region());
204   frame->set_capture_time_ms(
205       (TickTime::Now() - capture_start_time).Milliseconds());
206   callback_->OnCaptureCompleted(frame);
207
208   // Check for cursor shape update.
209   CaptureCursor();
210 }
211
212 void ScreenCapturerWin::SetMouseShapeObserver(
213       MouseShapeObserver* mouse_shape_observer) {
214   assert(!mouse_shape_observer_);
215   assert(mouse_shape_observer);
216
217   mouse_shape_observer_ = mouse_shape_observer;
218 }
219
220 bool ScreenCapturerWin::GetScreenList(ScreenList* screens) {
221   assert(screens->size() == 0);
222   BOOL enum_result = TRUE;
223   for (int device_index = 0; ; ++device_index) {
224     DISPLAY_DEVICE device;
225     device.cb = sizeof(device);
226     enum_result = EnumDisplayDevices(NULL, device_index, &device, 0);
227     // |enum_result| is 0 if we have enumerated all devices.
228     if (!enum_result)
229       break;
230
231     // We only care about active displays.
232     if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE))
233       continue;
234     Screen screen;
235     screen.id = device_index;
236     screens->push_back(screen);
237   }
238   return true;
239 }
240
241 bool ScreenCapturerWin::SelectScreen(ScreenId id) {
242   if (id == kFullDesktopScreenId) {
243     current_screen_id_ = id;
244     return true;
245   }
246   DISPLAY_DEVICE device;
247   device.cb = sizeof(device);
248   BOOL enum_result = EnumDisplayDevices(NULL, id, &device, 0);
249   if (!enum_result)
250     return false;
251
252   current_device_key_ = device.DeviceKey;
253   current_screen_id_ = id;
254   return true;
255 }
256
257 void ScreenCapturerWin::Start(Callback* callback) {
258   assert(!callback_);
259   assert(callback);
260
261   callback_ = callback;
262
263   // Vote to disable Aero composited desktop effects while capturing. Windows
264   // will restore Aero automatically if the process exits. This has no effect
265   // under Windows 8 or higher.  See crbug.com/124018.
266   if (composition_func_)
267     (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
268 }
269
270 void ScreenCapturerWin::PrepareCaptureResources() {
271   // Switch to the desktop receiving user input if different from the current
272   // one.
273   scoped_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
274   if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
275     // Release GDI resources otherwise SetThreadDesktop will fail.
276     if (desktop_dc_) {
277       ReleaseDC(NULL, desktop_dc_);
278       desktop_dc_ = NULL;
279     }
280
281     if (memory_dc_) {
282       DeleteDC(memory_dc_);
283       memory_dc_ = NULL;
284     }
285
286     // If SetThreadDesktop() fails, the thread is still assigned a desktop.
287     // So we can continue capture screen bits, just from the wrong desktop.
288     desktop_.SetThreadDesktop(input_desktop.release());
289
290     // Re-assert our vote to disable Aero.
291     // See crbug.com/124018 and crbug.com/129906.
292     if (composition_func_ != NULL) {
293       (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
294     }
295   }
296
297   // If the display bounds have changed then recreate GDI resources.
298   // TODO(wez): Also check for pixel format changes.
299   DesktopRect screen_rect(DesktopRect::MakeXYWH(
300       GetSystemMetrics(SM_XVIRTUALSCREEN),
301       GetSystemMetrics(SM_YVIRTUALSCREEN),
302       GetSystemMetrics(SM_CXVIRTUALSCREEN),
303       GetSystemMetrics(SM_CYVIRTUALSCREEN)));
304   if (!screen_rect.equals(desktop_dc_rect_)) {
305     if (desktop_dc_) {
306       ReleaseDC(NULL, desktop_dc_);
307       desktop_dc_ = NULL;
308     }
309     if (memory_dc_) {
310       DeleteDC(memory_dc_);
311       memory_dc_ = NULL;
312     }
313     desktop_dc_rect_ = DesktopRect();
314   }
315
316   if (desktop_dc_ == NULL) {
317     assert(memory_dc_ == NULL);
318
319     // Create GDI device contexts to capture from the desktop into memory.
320     desktop_dc_ = GetDC(NULL);
321     if (!desktop_dc_)
322       abort();
323     memory_dc_ = CreateCompatibleDC(desktop_dc_);
324     if (!memory_dc_)
325       abort();
326     desktop_dc_rect_ = screen_rect;
327
328     // Make sure the frame buffers will be reallocated.
329     queue_.Reset();
330
331     helper_.ClearInvalidRegion();
332   }
333 }
334
335 bool ScreenCapturerWin::CaptureImage() {
336   DesktopRect screen_rect = GetScreenRect();
337   if (screen_rect.is_empty())
338     return false;
339   DesktopSize size = screen_rect.size();
340   // If the current buffer is from an older generation then allocate a new one.
341   // Note that we can't reallocate other buffers at this point, since the caller
342   // may still be reading from them.
343   if (!queue_.current_frame() ||
344       !queue_.current_frame()->size().equals(size)) {
345     assert(desktop_dc_ != NULL);
346     assert(memory_dc_ != NULL);
347
348     size_t buffer_size = size.width() * size.height() *
349         DesktopFrame::kBytesPerPixel;
350     SharedMemory* shared_memory =
351         callback_->CreateSharedMemory(buffer_size);
352     scoped_ptr<DesktopFrameWin> buffer(
353         DesktopFrameWin::Create(size, shared_memory, desktop_dc_));
354     queue_.ReplaceCurrentFrame(buffer.release());
355   }
356
357   // Select the target bitmap into the memory dc and copy the rect from desktop
358   // to memory.
359   DesktopFrameWin* current = static_cast<DesktopFrameWin*>(
360       queue_.current_frame()->GetUnderlyingFrame());
361   HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap());
362   if (previous_object != NULL) {
363     BitBlt(memory_dc_,
364            0, 0, screen_rect.width(), screen_rect.height(),
365            desktop_dc_,
366            screen_rect.left(), screen_rect.top(),
367            SRCCOPY | CAPTUREBLT);
368
369     // Select back the previously selected object to that the device contect
370     // could be destroyed independently of the bitmap if needed.
371     SelectObject(memory_dc_, previous_object);
372   }
373   return true;
374 }
375
376 void ScreenCapturerWin::CaptureCursor() {
377   CURSORINFO cursor_info;
378   cursor_info.cbSize = sizeof(CURSORINFO);
379   if (!GetCursorInfo(&cursor_info)) {
380     LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError();
381     return;
382   }
383
384   // Note that |cursor_info.hCursor| does not need to be freed.
385   scoped_ptr<MouseCursor> cursor_image(
386       CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
387   if (!cursor_image.get())
388     return;
389
390   scoped_ptr<MouseCursorShape> cursor(new MouseCursorShape);
391   cursor->hotspot = cursor_image->hotspot();
392   cursor->size = cursor_image->image()->size();
393   uint8_t* current_row = cursor_image->image()->data();
394   for (int y = 0; y < cursor_image->image()->size().height(); ++y) {
395     cursor->data.append(current_row,
396                         current_row + cursor_image->image()->size().width() *
397                                         DesktopFrame::kBytesPerPixel);
398     current_row += cursor_image->image()->stride();
399   }
400
401   // Compare the current cursor with the last one we sent to the client. If
402   // they're the same, then don't bother sending the cursor again.
403   if (last_cursor_.size.equals(cursor->size) &&
404       last_cursor_.hotspot.equals(cursor->hotspot) &&
405       last_cursor_.data == cursor->data) {
406     return;
407   }
408
409   LOG(LS_VERBOSE) << "Sending updated cursor: " << cursor->size.width() << "x"
410                   << cursor->size.height();
411
412   // Record the last cursor image that we sent to the client.
413   last_cursor_ = *cursor;
414
415   if (mouse_shape_observer_)
416     mouse_shape_observer_->OnCursorShapeChanged(cursor.release());
417 }
418
419 DesktopRect ScreenCapturerWin::GetScreenRect() {
420   DesktopRect rect = desktop_dc_rect_;
421   if (current_screen_id_ == kFullDesktopScreenId)
422     return rect;
423
424   DISPLAY_DEVICE device;
425   device.cb = sizeof(device);
426   BOOL result = EnumDisplayDevices(NULL, current_screen_id_, &device, 0);
427   if (!result)
428     return DesktopRect();
429
430   // Verifies the device index still maps to the same display device. DeviceKey
431   // is documented as reserved, but it actually contains the registry key for
432   // the device and is unique for each monitor, while DeviceID is not.
433   if (current_device_key_ != device.DeviceKey)
434     return DesktopRect();
435
436   DEVMODE device_mode;
437   device_mode.dmSize = sizeof(device_mode);
438   device_mode.dmDriverExtra = 0;
439   result = EnumDisplaySettingsEx(
440       device.DeviceName, ENUM_CURRENT_SETTINGS, &device_mode, 0);
441   if (!result)
442     return DesktopRect();
443
444   rect = DesktopRect::MakeXYWH(
445       rect.left() + device_mode.dmPosition.x,
446       rect.top() + device_mode.dmPosition.y,
447       device_mode.dmPelsWidth,
448       device_mode.dmPelsHeight);
449   return rect;
450 }
451 }  // namespace
452
453 // static
454 ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
455   return new ScreenCapturerWin(options);
456 }
457
458 }  // namespace webrtc