Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / modules / desktop_capture / win / screen_capturer_win_gdi.cc
1 /*
2  *  Copyright (c) 2014 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/win/screen_capturer_win_gdi.h"
12
13 #include <assert.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/win/cursor.h"
22 #include "webrtc/modules/desktop_capture/win/desktop.h"
23 #include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
24 #include "webrtc/system_wrappers/interface/logging.h"
25 #include "webrtc/system_wrappers/interface/tick_util.h"
26
27 namespace webrtc {
28
29 namespace {
30
31 // Constants from dwmapi.h.
32 const UINT DWM_EC_DISABLECOMPOSITION = 0;
33 const UINT DWM_EC_ENABLECOMPOSITION = 1;
34
35 const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll";
36
37 }  // namespace
38
39 ScreenCapturerWinGdi::ScreenCapturerWinGdi(const DesktopCaptureOptions& options)
40     : callback_(NULL),
41       mouse_shape_observer_(NULL),
42       current_screen_id_(kFullDesktopScreenId),
43       desktop_dc_(NULL),
44       memory_dc_(NULL),
45       dwmapi_library_(NULL),
46       composition_func_(NULL),
47       set_thread_execution_state_failed_(false) {
48   if (options.disable_effects()) {
49     // Load dwmapi.dll dynamically since it is not available on XP.
50     if (!dwmapi_library_)
51       dwmapi_library_ = LoadLibrary(kDwmapiLibraryName);
52
53     if (dwmapi_library_) {
54       composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
55           GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
56     }
57   }
58 }
59
60 ScreenCapturerWinGdi::~ScreenCapturerWinGdi() {
61   if (desktop_dc_)
62     ReleaseDC(NULL, desktop_dc_);
63   if (memory_dc_)
64     DeleteDC(memory_dc_);
65
66   // Restore Aero.
67   if (composition_func_)
68     (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
69
70   if (dwmapi_library_)
71     FreeLibrary(dwmapi_library_);
72 }
73
74 void ScreenCapturerWinGdi::Capture(const DesktopRegion& region) {
75   TickTime capture_start_time = TickTime::Now();
76
77   queue_.MoveToNextFrame();
78
79   // Request that the system not power-down the system, or the display hardware.
80   if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) {
81     if (!set_thread_execution_state_failed_) {
82       set_thread_execution_state_failed_ = true;
83       LOG_F(LS_WARNING) << "Failed to make system & display power assertion: "
84                         << GetLastError();
85     }
86   }
87
88   // Make sure the GDI capture resources are up-to-date.
89   PrepareCaptureResources();
90
91   if (!CaptureImage()) {
92     callback_->OnCaptureCompleted(NULL);
93     return;
94   }
95
96   const DesktopFrame* current_frame = queue_.current_frame();
97   const DesktopFrame* last_frame = queue_.previous_frame();
98   if (last_frame && last_frame->size().equals(current_frame->size())) {
99     // Make sure the differencer is set up correctly for these previous and
100     // current screens.
101     if (!differ_.get() ||
102         (differ_->width() != current_frame->size().width()) ||
103         (differ_->height() != current_frame->size().height()) ||
104         (differ_->bytes_per_row() != current_frame->stride())) {
105       differ_.reset(new Differ(current_frame->size().width(),
106                                current_frame->size().height(),
107                                DesktopFrame::kBytesPerPixel,
108                                current_frame->stride()));
109     }
110
111     // Calculate difference between the two last captured frames.
112     DesktopRegion region;
113     differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(),
114                              &region);
115     helper_.InvalidateRegion(region);
116   } else {
117     // No previous frame is available, or the screen is resized. Invalidate the
118     // whole screen.
119     helper_.InvalidateScreen(current_frame->size());
120   }
121
122   helper_.set_size_most_recent(current_frame->size());
123
124   // Emit the current frame.
125   DesktopFrame* frame = queue_.current_frame()->Share();
126   frame->set_dpi(DesktopVector(
127       GetDeviceCaps(desktop_dc_, LOGPIXELSX),
128       GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
129   frame->mutable_updated_region()->Clear();
130   helper_.TakeInvalidRegion(frame->mutable_updated_region());
131   frame->set_capture_time_ms(
132       (TickTime::Now() - capture_start_time).Milliseconds());
133   callback_->OnCaptureCompleted(frame);
134
135   // Check for cursor shape update.
136   if (mouse_shape_observer_)
137     CaptureCursor();
138 }
139
140 void ScreenCapturerWinGdi::SetMouseShapeObserver(
141     MouseShapeObserver* mouse_shape_observer) {
142   assert(!mouse_shape_observer_);
143   assert(mouse_shape_observer);
144
145   mouse_shape_observer_ = mouse_shape_observer;
146 }
147
148 bool ScreenCapturerWinGdi::GetScreenList(ScreenList* screens) {
149   return webrtc::GetScreenList(screens);
150 }
151
152 bool ScreenCapturerWinGdi::SelectScreen(ScreenId id) {
153   bool valid = IsScreenValid(id, &current_device_key_);
154   if (valid)
155     current_screen_id_ = id;
156   return valid;
157 }
158
159 void ScreenCapturerWinGdi::Start(Callback* callback) {
160   assert(!callback_);
161   assert(callback);
162
163   callback_ = callback;
164
165   // Vote to disable Aero composited desktop effects while capturing. Windows
166   // will restore Aero automatically if the process exits. This has no effect
167   // under Windows 8 or higher.  See crbug.com/124018.
168   if (composition_func_)
169     (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
170 }
171
172 void ScreenCapturerWinGdi::PrepareCaptureResources() {
173   // Switch to the desktop receiving user input if different from the current
174   // one.
175   scoped_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
176   if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
177     // Release GDI resources otherwise SetThreadDesktop will fail.
178     if (desktop_dc_) {
179       ReleaseDC(NULL, desktop_dc_);
180       desktop_dc_ = NULL;
181     }
182
183     if (memory_dc_) {
184       DeleteDC(memory_dc_);
185       memory_dc_ = NULL;
186     }
187
188     // If SetThreadDesktop() fails, the thread is still assigned a desktop.
189     // So we can continue capture screen bits, just from the wrong desktop.
190     desktop_.SetThreadDesktop(input_desktop.release());
191
192     // Re-assert our vote to disable Aero.
193     // See crbug.com/124018 and crbug.com/129906.
194     if (composition_func_ != NULL) {
195       (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
196     }
197   }
198
199   // If the display bounds have changed then recreate GDI resources.
200   // TODO(wez): Also check for pixel format changes.
201   DesktopRect screen_rect(DesktopRect::MakeXYWH(
202       GetSystemMetrics(SM_XVIRTUALSCREEN),
203       GetSystemMetrics(SM_YVIRTUALSCREEN),
204       GetSystemMetrics(SM_CXVIRTUALSCREEN),
205       GetSystemMetrics(SM_CYVIRTUALSCREEN)));
206   if (!screen_rect.equals(desktop_dc_rect_)) {
207     if (desktop_dc_) {
208       ReleaseDC(NULL, desktop_dc_);
209       desktop_dc_ = NULL;
210     }
211     if (memory_dc_) {
212       DeleteDC(memory_dc_);
213       memory_dc_ = NULL;
214     }
215     desktop_dc_rect_ = DesktopRect();
216   }
217
218   if (desktop_dc_ == NULL) {
219     assert(memory_dc_ == NULL);
220
221     // Create GDI device contexts to capture from the desktop into memory.
222     desktop_dc_ = GetDC(NULL);
223     if (!desktop_dc_)
224       abort();
225     memory_dc_ = CreateCompatibleDC(desktop_dc_);
226     if (!memory_dc_)
227       abort();
228
229     desktop_dc_rect_ = screen_rect;
230
231     // Make sure the frame buffers will be reallocated.
232     queue_.Reset();
233
234     helper_.ClearInvalidRegion();
235   }
236 }
237
238 bool ScreenCapturerWinGdi::CaptureImage() {
239   DesktopRect screen_rect =
240       GetScreenRect(current_screen_id_, current_device_key_);
241   if (screen_rect.is_empty())
242     return false;
243
244   DesktopSize size = screen_rect.size();
245   // If the current buffer is from an older generation then allocate a new one.
246   // Note that we can't reallocate other buffers at this point, since the caller
247   // may still be reading from them.
248   if (!queue_.current_frame() ||
249       !queue_.current_frame()->size().equals(screen_rect.size())) {
250     assert(desktop_dc_ != NULL);
251     assert(memory_dc_ != NULL);
252
253     size_t buffer_size = size.width() * size.height() *
254         DesktopFrame::kBytesPerPixel;
255     SharedMemory* shared_memory = callback_->CreateSharedMemory(buffer_size);
256
257     scoped_ptr<DesktopFrame> buffer;
258     buffer.reset(
259         DesktopFrameWin::Create(size, shared_memory, desktop_dc_));
260     queue_.ReplaceCurrentFrame(buffer.release());
261   }
262
263   // Select the target bitmap into the memory dc and copy the rect from desktop
264   // to memory.
265   DesktopFrameWin* current = static_cast<DesktopFrameWin*>(
266       queue_.current_frame()->GetUnderlyingFrame());
267   HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap());
268   if (previous_object != NULL) {
269     BitBlt(memory_dc_,
270            0, 0, screen_rect.width(), screen_rect.height(),
271            desktop_dc_,
272            screen_rect.left(), screen_rect.top(),
273            SRCCOPY | CAPTUREBLT);
274
275     // Select back the previously selected object to that the device contect
276     // could be destroyed independently of the bitmap if needed.
277     SelectObject(memory_dc_, previous_object);
278   }
279   return true;
280 }
281
282 void ScreenCapturerWinGdi::CaptureCursor() {
283   assert(mouse_shape_observer_);
284
285   CURSORINFO cursor_info;
286   cursor_info.cbSize = sizeof(CURSORINFO);
287   if (!GetCursorInfo(&cursor_info)) {
288     LOG_F(LS_ERROR) << "Unable to get cursor info. Error = " << GetLastError();
289     return;
290   }
291
292   // Note that |cursor_info.hCursor| does not need to be freed.
293   scoped_ptr<MouseCursor> cursor_image(
294       CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
295   if (!cursor_image.get())
296     return;
297
298   scoped_ptr<MouseCursorShape> cursor(new MouseCursorShape);
299   cursor->hotspot = cursor_image->hotspot();
300   cursor->size = cursor_image->image()->size();
301   uint8_t* current_row = cursor_image->image()->data();
302   for (int y = 0; y < cursor_image->image()->size().height(); ++y) {
303     cursor->data.append(current_row,
304                         current_row + cursor_image->image()->size().width() *
305                                         DesktopFrame::kBytesPerPixel);
306     current_row += cursor_image->image()->stride();
307   }
308
309   // Compare the current cursor with the last one we sent to the client. If
310   // they're the same, then don't bother sending the cursor again.
311   if (last_cursor_.size.equals(cursor->size) &&
312       last_cursor_.hotspot.equals(cursor->hotspot) &&
313       last_cursor_.data == cursor->data) {
314     return;
315   }
316
317   LOG(LS_VERBOSE) << "Sending updated cursor: " << cursor->size.width() << "x"
318                   << cursor->size.height();
319
320   // Record the last cursor image that we sent to the client.
321   last_cursor_ = *cursor;
322
323   mouse_shape_observer_->OnCursorShapeChanged(cursor.release());
324 }
325
326 }  // namespace webrtc