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