Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / webrtc / modules / desktop_capture / win / screen_capturer_win_magnifier.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_magnifier.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 // kMagnifierWindowClass has to be "Magnifier" according to the Magnification
30 // API. The other strings can be anything.
31 static LPCTSTR kMagnifierHostClass = L"ScreenCapturerWinMagnifierHost";
32 static LPCTSTR kHostWindowName = L"MagnifierHost";
33 static LPCTSTR kMagnifierWindowClass = L"Magnifier";
34 static LPCTSTR kMagnifierWindowName = L"MagnifierWindow";
35
36 Atomic32 ScreenCapturerWinMagnifier::tls_index_(TLS_OUT_OF_INDEXES);
37
38 ScreenCapturerWinMagnifier::ScreenCapturerWinMagnifier(
39     scoped_ptr<ScreenCapturer> fallback_capturer)
40     : fallback_capturer_(fallback_capturer.Pass()),
41       fallback_capturer_started_(false),
42       callback_(NULL),
43       current_screen_id_(kFullDesktopScreenId),
44       excluded_window_(NULL),
45       set_thread_execution_state_failed_(false),
46       desktop_dc_(NULL),
47       mag_lib_handle_(NULL),
48       mag_initialize_func_(NULL),
49       mag_uninitialize_func_(NULL),
50       set_window_source_func_(NULL),
51       set_window_filter_list_func_(NULL),
52       set_image_scaling_callback_func_(NULL),
53       host_window_(NULL),
54       magnifier_window_(NULL),
55       magnifier_initialized_(false),
56       magnifier_capture_succeeded_(true) {
57 }
58
59 ScreenCapturerWinMagnifier::~ScreenCapturerWinMagnifier() {
60   // DestroyWindow must be called before MagUninitialize. magnifier_window_ is
61   // destroyed automatically when host_window_ is destroyed.
62   if (host_window_)
63     DestroyWindow(host_window_);
64
65   if (magnifier_initialized_)
66     mag_uninitialize_func_();
67
68   if (mag_lib_handle_)
69     FreeLibrary(mag_lib_handle_);
70
71   if (desktop_dc_)
72     ReleaseDC(NULL, desktop_dc_);
73 }
74
75 void ScreenCapturerWinMagnifier::Start(Callback* callback) {
76   assert(!callback_);
77   assert(callback);
78   callback_ = callback;
79
80   InitializeMagnifier();
81 }
82
83 void ScreenCapturerWinMagnifier::Capture(const DesktopRegion& region) {
84   TickTime capture_start_time = TickTime::Now();
85
86   queue_.MoveToNextFrame();
87
88   // Request that the system not power-down the system, or the display hardware.
89   if (!SetThreadExecutionState(ES_DISPLAY_REQUIRED | ES_SYSTEM_REQUIRED)) {
90     if (!set_thread_execution_state_failed_) {
91       set_thread_execution_state_failed_ = true;
92       LOG_F(LS_WARNING) << "Failed to make system & display power assertion: "
93                         << GetLastError();
94     }
95   }
96   // Switch to the desktop receiving user input if different from the current
97   // one.
98   scoped_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
99   if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
100     // Release GDI resources otherwise SetThreadDesktop will fail.
101     if (desktop_dc_) {
102       ReleaseDC(NULL, desktop_dc_);
103       desktop_dc_ = NULL;
104     }
105     // If SetThreadDesktop() fails, the thread is still assigned a desktop.
106     // So we can continue capture screen bits, just from the wrong desktop.
107     desktop_.SetThreadDesktop(input_desktop.release());
108   }
109
110   bool succeeded = false;
111
112   // Do not try to use the magnfiier if it's capturing non-primary screen, or it
113   // failed before.
114   if (magnifier_initialized_ && IsCapturingPrimaryScreenOnly() &&
115       magnifier_capture_succeeded_) {
116     DesktopRect rect = GetScreenRect(current_screen_id_, current_device_key_);
117     CreateCurrentFrameIfNecessary(rect.size());
118
119     // CaptureImage may fail in some situations, e.g. windows8 metro mode.
120     succeeded = CaptureImage(rect);
121   }
122
123   // Defer to the fallback capturer if magnifier capturer did not work.
124   if (!succeeded) {
125     LOG_F(LS_WARNING) << "Switching to the fallback screen capturer.";
126     StartFallbackCapturer();
127     fallback_capturer_->Capture(region);
128     return;
129   }
130
131   const DesktopFrame* current_frame = queue_.current_frame();
132   const DesktopFrame* last_frame = queue_.previous_frame();
133   if (last_frame && last_frame->size().equals(current_frame->size())) {
134     // Make sure the differencer is set up correctly for these previous and
135     // current screens.
136     if (!differ_.get() || (differ_->width() != current_frame->size().width()) ||
137         (differ_->height() != current_frame->size().height()) ||
138         (differ_->bytes_per_row() != current_frame->stride())) {
139       differ_.reset(new Differ(current_frame->size().width(),
140                                current_frame->size().height(),
141                                DesktopFrame::kBytesPerPixel,
142                                current_frame->stride()));
143     }
144
145     // Calculate difference between the two last captured frames.
146     DesktopRegion region;
147     differ_->CalcDirtyRegion(
148         last_frame->data(), current_frame->data(), &region);
149     helper_.InvalidateRegion(region);
150   } else {
151     // No previous frame is available, or the screen is resized. Invalidate the
152     // whole screen.
153     helper_.InvalidateScreen(current_frame->size());
154   }
155
156   helper_.set_size_most_recent(current_frame->size());
157
158   // Emit the current frame.
159   DesktopFrame* frame = queue_.current_frame()->Share();
160   frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
161                                GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
162   frame->mutable_updated_region()->Clear();
163   helper_.TakeInvalidRegion(frame->mutable_updated_region());
164   frame->set_capture_time_ms(
165       (TickTime::Now() - capture_start_time).Milliseconds());
166   callback_->OnCaptureCompleted(frame);
167 }
168
169 bool ScreenCapturerWinMagnifier::GetScreenList(ScreenList* screens) {
170   return webrtc::GetScreenList(screens);
171 }
172
173 bool ScreenCapturerWinMagnifier::SelectScreen(ScreenId id) {
174   bool valid = IsScreenValid(id, &current_device_key_);
175
176   // Set current_screen_id_ even if the fallback capturer is being used, so we
177   // can switch back to the magnifier when possible.
178   if (valid)
179     current_screen_id_ = id;
180
181   if (fallback_capturer_started_)
182     fallback_capturer_->SelectScreen(id);
183
184   return valid;
185 }
186
187 void ScreenCapturerWinMagnifier::SetExcludedWindow(WindowId excluded_window) {
188   excluded_window_ = (HWND)excluded_window;
189   if (excluded_window_ && magnifier_initialized_) {
190     set_window_filter_list_func_(
191         magnifier_window_, MW_FILTERMODE_EXCLUDE, 1, &excluded_window_);
192   }
193 }
194
195 bool ScreenCapturerWinMagnifier::CaptureImage(const DesktopRect& rect) {
196   assert(magnifier_initialized_);
197
198   // Set the magnifier control to cover the captured rect. The content of the
199   // magnifier control will be the captured image.
200   BOOL result = SetWindowPos(magnifier_window_,
201                              NULL,
202                              rect.left(), rect.top(),
203                              rect.width(), rect.height(),
204                              0);
205   if (!result) {
206     LOG_F(LS_WARNING) << "Failed to call SetWindowPos: " << GetLastError()
207                       << ". Rect = {" << rect.left() << ", " << rect.top()
208                       << ", " << rect.right() << ", " << rect.bottom() << "}";
209     return false;
210   }
211
212   magnifier_capture_succeeded_ = false;
213
214   RECT native_rect = {rect.left(), rect.top(), rect.right(), rect.bottom()};
215
216   // OnCaptured will be called via OnMagImageScalingCallback and fill in the
217   // frame before set_window_source_func_ returns.
218   result = set_window_source_func_(magnifier_window_, native_rect);
219
220   if (!result) {
221     LOG_F(LS_WARNING) << "Failed to call MagSetWindowSource: " << GetLastError()
222                       << ". Rect = {" << rect.left() << ", " << rect.top()
223                       << ", " << rect.right() << ", " << rect.bottom() << "}";
224     return false;
225   }
226
227   return magnifier_capture_succeeded_;
228 }
229
230 BOOL ScreenCapturerWinMagnifier::OnMagImageScalingCallback(
231     HWND hwnd,
232     void* srcdata,
233     MAGIMAGEHEADER srcheader,
234     void* destdata,
235     MAGIMAGEHEADER destheader,
236     RECT unclipped,
237     RECT clipped,
238     HRGN dirty) {
239   assert(tls_index_.Value() != TLS_OUT_OF_INDEXES);
240
241   ScreenCapturerWinMagnifier* owner =
242       reinterpret_cast<ScreenCapturerWinMagnifier*>(
243           TlsGetValue(tls_index_.Value()));
244
245   owner->OnCaptured(srcdata, srcheader);
246
247   return TRUE;
248 }
249
250 bool ScreenCapturerWinMagnifier::InitializeMagnifier() {
251   assert(!magnifier_initialized_);
252
253   desktop_dc_ = GetDC(NULL);
254
255   mag_lib_handle_ = LoadLibrary(L"Magnification.dll");
256   if (!mag_lib_handle_)
257     return false;
258
259   // Initialize Magnification API function pointers.
260   mag_initialize_func_ = reinterpret_cast<MagInitializeFunc>(
261       GetProcAddress(mag_lib_handle_, "MagInitialize"));
262   mag_uninitialize_func_ = reinterpret_cast<MagUninitializeFunc>(
263       GetProcAddress(mag_lib_handle_, "MagUninitialize"));
264   set_window_source_func_ = reinterpret_cast<MagSetWindowSourceFunc>(
265       GetProcAddress(mag_lib_handle_, "MagSetWindowSource"));
266   set_window_filter_list_func_ = reinterpret_cast<MagSetWindowFilterListFunc>(
267       GetProcAddress(mag_lib_handle_, "MagSetWindowFilterList"));
268   set_image_scaling_callback_func_ =
269       reinterpret_cast<MagSetImageScalingCallbackFunc>(
270           GetProcAddress(mag_lib_handle_, "MagSetImageScalingCallback"));
271
272   if (!mag_initialize_func_ || !mag_uninitialize_func_ ||
273       !set_window_source_func_ || !set_window_filter_list_func_ ||
274       !set_image_scaling_callback_func_) {
275     LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
276                       << "library functions missing.";
277     return false;
278   }
279
280   BOOL result = mag_initialize_func_();
281   if (!result) {
282     LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
283                       << "error from MagInitialize " << GetLastError();
284     return false;
285   }
286
287   HMODULE hInstance = NULL;
288   result = GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
289                                   GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
290                               reinterpret_cast<char*>(&DefWindowProc),
291                               &hInstance);
292   if (!result) {
293     mag_uninitialize_func_();
294     LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
295                       << "error from GetModulehandleExA " << GetLastError();
296     return false;
297   }
298
299   // Register the host window class. See the MSDN documentation of the
300   // Magnification API for more infomation.
301   WNDCLASSEX wcex = {};
302   wcex.cbSize = sizeof(WNDCLASSEX);
303   wcex.lpfnWndProc = &DefWindowProc;
304   wcex.hInstance = hInstance;
305   wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
306   wcex.lpszClassName = kMagnifierHostClass;
307
308   // Ignore the error which may happen when the class is already registered.
309   RegisterClassEx(&wcex);
310
311   // Create the host window.
312   host_window_ = CreateWindowEx(WS_EX_LAYERED,
313                                 kMagnifierHostClass,
314                                 kHostWindowName,
315                                 0,
316                                 0, 0, 0, 0,
317                                 NULL,
318                                 NULL,
319                                 hInstance,
320                                 NULL);
321   if (!host_window_) {
322     mag_uninitialize_func_();
323     LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
324                       << "error from creating host window " << GetLastError();
325     return false;
326   }
327
328   // Create the magnifier control.
329   magnifier_window_ = CreateWindow(kMagnifierWindowClass,
330                                    kMagnifierWindowName,
331                                    WS_CHILD | WS_VISIBLE,
332                                    0, 0, 0, 0,
333                                    host_window_,
334                                    NULL,
335                                    hInstance,
336                                    NULL);
337   if (!magnifier_window_) {
338     mag_uninitialize_func_();
339     LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
340                       << "error from creating magnifier window "
341                       << GetLastError();
342     return false;
343   }
344
345   // Hide the host window.
346   ShowWindow(host_window_, SW_HIDE);
347
348   // Set the scaling callback to receive captured image.
349   result = set_image_scaling_callback_func_(
350       magnifier_window_,
351       &ScreenCapturerWinMagnifier::OnMagImageScalingCallback);
352   if (!result) {
353     mag_uninitialize_func_();
354     LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
355                       << "error from MagSetImageScalingCallback "
356                       << GetLastError();
357     return false;
358   }
359
360   if (excluded_window_) {
361     result = set_window_filter_list_func_(
362         magnifier_window_, MW_FILTERMODE_EXCLUDE, 1, &excluded_window_);
363     if (!result) {
364       mag_uninitialize_func_();
365       LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
366                         << "error from MagSetWindowFilterList "
367                         << GetLastError();
368       return false;
369     }
370   }
371
372   if (tls_index_.Value() == TLS_OUT_OF_INDEXES) {
373     // More than one threads may get here at the same time, but only one will
374     // write to tls_index_ using CompareExchange.
375     DWORD new_tls_index = TlsAlloc();
376     if (!tls_index_.CompareExchange(new_tls_index, TLS_OUT_OF_INDEXES))
377       TlsFree(new_tls_index);
378   }
379
380   assert(tls_index_.Value() != TLS_OUT_OF_INDEXES);
381   TlsSetValue(tls_index_.Value(), this);
382
383   magnifier_initialized_ = true;
384   return true;
385 }
386
387 void ScreenCapturerWinMagnifier::OnCaptured(void* data,
388                                             const MAGIMAGEHEADER& header) {
389   DesktopFrame* current_frame = queue_.current_frame();
390
391   // Verify the format.
392   // TODO(jiayl): support capturing sources with pixel formats other than RGBA.
393   int captured_bytes_per_pixel = header.cbSize / header.width / header.height;
394   if (header.format != GUID_WICPixelFormat32bppRGBA ||
395       header.width != static_cast<UINT>(current_frame->size().width()) ||
396       header.height != static_cast<UINT>(current_frame->size().height()) ||
397       header.stride != static_cast<UINT>(current_frame->stride()) ||
398       captured_bytes_per_pixel != DesktopFrame::kBytesPerPixel) {
399     LOG_F(LS_WARNING) << "Output format does not match the captured format: "
400                       << "width = " << header.width << ", "
401                       << "height = " << header.height << ", "
402                       << "stride = " << header.stride << ", "
403                       << "bpp = " << captured_bytes_per_pixel << ", "
404                       << "pixel format RGBA ? "
405                       << (header.format == GUID_WICPixelFormat32bppRGBA) << ".";
406     return;
407   }
408
409   // Copy the data into the frame.
410   current_frame->CopyPixelsFrom(
411       reinterpret_cast<uint8_t*>(data),
412       header.stride,
413       DesktopRect::MakeXYWH(0, 0, header.width, header.height));
414
415   magnifier_capture_succeeded_ = true;
416 }
417
418 void ScreenCapturerWinMagnifier::CreateCurrentFrameIfNecessary(
419     const DesktopSize& size) {
420   // If the current buffer is from an older generation then allocate a new one.
421   // Note that we can't reallocate other buffers at this point, since the caller
422   // may still be reading from them.
423   if (!queue_.current_frame() || !queue_.current_frame()->size().equals(size)) {
424     size_t buffer_size =
425         size.width() * size.height() * DesktopFrame::kBytesPerPixel;
426     SharedMemory* shared_memory = callback_->CreateSharedMemory(buffer_size);
427
428     scoped_ptr<DesktopFrame> buffer;
429     if (shared_memory) {
430       buffer.reset(new SharedMemoryDesktopFrame(
431           size, size.width() * DesktopFrame::kBytesPerPixel, shared_memory));
432     } else {
433       buffer.reset(new BasicDesktopFrame(size));
434     }
435     queue_.ReplaceCurrentFrame(buffer.release());
436   }
437 }
438
439 bool ScreenCapturerWinMagnifier::IsCapturingPrimaryScreenOnly() const {
440   if (current_screen_id_ != kFullDesktopScreenId)
441     return current_screen_id_ == 0;  // the primary screen is always '0'.
442
443   return GetSystemMetrics(SM_CMONITORS) == 1;
444 }
445
446 void ScreenCapturerWinMagnifier::StartFallbackCapturer() {
447   assert(fallback_capturer_);
448   if (!fallback_capturer_started_) {
449     fallback_capturer_started_ = true;
450
451     fallback_capturer_->Start(callback_);
452     fallback_capturer_->SelectScreen(current_screen_id_);
453   }
454 }
455
456 }  // namespace webrtc