Update To 11.40.268.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       current_screen_id_(kFullDesktopScreenId),
42       desktop_dc_(NULL),
43       memory_dc_(NULL),
44       dwmapi_library_(NULL),
45       composition_func_(NULL),
46       set_thread_execution_state_failed_(false) {
47   if (options.disable_effects()) {
48     // Load dwmapi.dll dynamically since it is not available on XP.
49     if (!dwmapi_library_)
50       dwmapi_library_ = LoadLibrary(kDwmapiLibraryName);
51
52     if (dwmapi_library_) {
53       composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
54           GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
55     }
56   }
57 }
58
59 ScreenCapturerWinGdi::~ScreenCapturerWinGdi() {
60   if (desktop_dc_)
61     ReleaseDC(NULL, desktop_dc_);
62   if (memory_dc_)
63     DeleteDC(memory_dc_);
64
65   // Restore Aero.
66   if (composition_func_)
67     (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
68
69   if (dwmapi_library_)
70     FreeLibrary(dwmapi_library_);
71 }
72
73 void ScreenCapturerWinGdi::Capture(const DesktopRegion& region) {
74   TickTime capture_start_time = TickTime::Now();
75
76   queue_.MoveToNextFrame();
77
78   // Request that the system not power-down the system, or the display hardware.
79   if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) {
80     if (!set_thread_execution_state_failed_) {
81       set_thread_execution_state_failed_ = true;
82       LOG_F(LS_WARNING) << "Failed to make system & display power assertion: "
83                         << GetLastError();
84     }
85   }
86
87   // Make sure the GDI capture resources are up-to-date.
88   PrepareCaptureResources();
89
90   if (!CaptureImage()) {
91     callback_->OnCaptureCompleted(NULL);
92     return;
93   }
94
95   const DesktopFrame* current_frame = queue_.current_frame();
96   const DesktopFrame* last_frame = queue_.previous_frame();
97   if (last_frame && last_frame->size().equals(current_frame->size())) {
98     // Make sure the differencer is set up correctly for these previous and
99     // current screens.
100     if (!differ_.get() ||
101         (differ_->width() != current_frame->size().width()) ||
102         (differ_->height() != current_frame->size().height()) ||
103         (differ_->bytes_per_row() != current_frame->stride())) {
104       differ_.reset(new Differ(current_frame->size().width(),
105                                current_frame->size().height(),
106                                DesktopFrame::kBytesPerPixel,
107                                current_frame->stride()));
108     }
109
110     // Calculate difference between the two last captured frames.
111     DesktopRegion region;
112     differ_->CalcDirtyRegion(last_frame->data(), current_frame->data(),
113                              &region);
114     helper_.InvalidateRegion(region);
115   } else {
116     // No previous frame is available, or the screen is resized. Invalidate the
117     // whole screen.
118     helper_.InvalidateScreen(current_frame->size());
119   }
120
121   helper_.set_size_most_recent(current_frame->size());
122
123   // Emit the current frame.
124   DesktopFrame* frame = queue_.current_frame()->Share();
125   frame->set_dpi(DesktopVector(
126       GetDeviceCaps(desktop_dc_, LOGPIXELSX),
127       GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
128   frame->mutable_updated_region()->Clear();
129   helper_.TakeInvalidRegion(frame->mutable_updated_region());
130   frame->set_capture_time_ms(
131       (TickTime::Now() - capture_start_time).Milliseconds());
132   callback_->OnCaptureCompleted(frame);
133 }
134
135 bool ScreenCapturerWinGdi::GetScreenList(ScreenList* screens) {
136   return webrtc::GetScreenList(screens);
137 }
138
139 bool ScreenCapturerWinGdi::SelectScreen(ScreenId id) {
140   bool valid = IsScreenValid(id, &current_device_key_);
141   if (valid)
142     current_screen_id_ = id;
143   return valid;
144 }
145
146 void ScreenCapturerWinGdi::Start(Callback* callback) {
147   assert(!callback_);
148   assert(callback);
149
150   callback_ = callback;
151
152   // Vote to disable Aero composited desktop effects while capturing. Windows
153   // will restore Aero automatically if the process exits. This has no effect
154   // under Windows 8 or higher.  See crbug.com/124018.
155   if (composition_func_)
156     (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
157 }
158
159 void ScreenCapturerWinGdi::PrepareCaptureResources() {
160   // Switch to the desktop receiving user input if different from the current
161   // one.
162   scoped_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
163   if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
164     // Release GDI resources otherwise SetThreadDesktop will fail.
165     if (desktop_dc_) {
166       ReleaseDC(NULL, desktop_dc_);
167       desktop_dc_ = NULL;
168     }
169
170     if (memory_dc_) {
171       DeleteDC(memory_dc_);
172       memory_dc_ = NULL;
173     }
174
175     // If SetThreadDesktop() fails, the thread is still assigned a desktop.
176     // So we can continue capture screen bits, just from the wrong desktop.
177     desktop_.SetThreadDesktop(input_desktop.release());
178
179     // Re-assert our vote to disable Aero.
180     // See crbug.com/124018 and crbug.com/129906.
181     if (composition_func_ != NULL) {
182       (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
183     }
184   }
185
186   // If the display bounds have changed then recreate GDI resources.
187   // TODO(wez): Also check for pixel format changes.
188   DesktopRect screen_rect(DesktopRect::MakeXYWH(
189       GetSystemMetrics(SM_XVIRTUALSCREEN),
190       GetSystemMetrics(SM_YVIRTUALSCREEN),
191       GetSystemMetrics(SM_CXVIRTUALSCREEN),
192       GetSystemMetrics(SM_CYVIRTUALSCREEN)));
193   if (!screen_rect.equals(desktop_dc_rect_)) {
194     if (desktop_dc_) {
195       ReleaseDC(NULL, desktop_dc_);
196       desktop_dc_ = NULL;
197     }
198     if (memory_dc_) {
199       DeleteDC(memory_dc_);
200       memory_dc_ = NULL;
201     }
202     desktop_dc_rect_ = DesktopRect();
203   }
204
205   if (desktop_dc_ == NULL) {
206     assert(memory_dc_ == NULL);
207
208     // Create GDI device contexts to capture from the desktop into memory.
209     desktop_dc_ = GetDC(NULL);
210     if (!desktop_dc_)
211       abort();
212     memory_dc_ = CreateCompatibleDC(desktop_dc_);
213     if (!memory_dc_)
214       abort();
215
216     desktop_dc_rect_ = screen_rect;
217
218     // Make sure the frame buffers will be reallocated.
219     queue_.Reset();
220
221     helper_.ClearInvalidRegion();
222   }
223 }
224
225 bool ScreenCapturerWinGdi::CaptureImage() {
226   DesktopRect screen_rect =
227       GetScreenRect(current_screen_id_, current_device_key_);
228   if (screen_rect.is_empty())
229     return false;
230
231   DesktopSize size = screen_rect.size();
232   // If the current buffer is from an older generation then allocate a new one.
233   // Note that we can't reallocate other buffers at this point, since the caller
234   // may still be reading from them.
235   if (!queue_.current_frame() ||
236       !queue_.current_frame()->size().equals(screen_rect.size())) {
237     assert(desktop_dc_ != NULL);
238     assert(memory_dc_ != NULL);
239
240     size_t buffer_size = size.width() * size.height() *
241         DesktopFrame::kBytesPerPixel;
242     SharedMemory* shared_memory = callback_->CreateSharedMemory(buffer_size);
243
244     scoped_ptr<DesktopFrame> buffer;
245     buffer.reset(
246         DesktopFrameWin::Create(size, shared_memory, desktop_dc_));
247     queue_.ReplaceCurrentFrame(buffer.release());
248   }
249
250   // Select the target bitmap into the memory dc and copy the rect from desktop
251   // to memory.
252   DesktopFrameWin* current = static_cast<DesktopFrameWin*>(
253       queue_.current_frame()->GetUnderlyingFrame());
254   HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap());
255   if (previous_object != NULL) {
256     BitBlt(memory_dc_,
257            0, 0, screen_rect.width(), screen_rect.height(),
258            desktop_dc_,
259            screen_rect.left(), screen_rect.top(),
260            SRCCOPY | CAPTUREBLT);
261
262     // Select back the previously selected object to that the device contect
263     // could be destroyed independently of the bitmap if needed.
264     SelectObject(memory_dc_, previous_object);
265   }
266   return true;
267 }
268
269 }  // namespace webrtc