screen_capturer_win_gdi.cc (8402B)
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 "modules/desktop_capture/win/screen_capturer_win_gdi.h" 12 13 #include <cstddef> 14 #include <cstdint> 15 #include <memory> 16 #include <optional> 17 #include <string> 18 #include <utility> 19 20 #include "modules/desktop_capture/desktop_capture_metrics_helper.h" 21 #include "modules/desktop_capture/desktop_capture_options.h" 22 #include "modules/desktop_capture/desktop_capture_types.h" 23 #include "modules/desktop_capture/desktop_frame.h" 24 #include "modules/desktop_capture/desktop_frame_win.h" 25 #include "modules/desktop_capture/desktop_geometry.h" 26 #include "modules/desktop_capture/desktop_region.h" 27 #include "modules/desktop_capture/shared_desktop_frame.h" 28 #include "modules/desktop_capture/shared_memory.h" 29 #include "modules/desktop_capture/win/desktop.h" 30 #include "modules/desktop_capture/win/screen_capture_utils.h" 31 #include "rtc_base/checks.h" 32 #include "rtc_base/logging.h" 33 #include "rtc_base/time_utils.h" 34 #include "rtc_base/trace_event.h" 35 #include "system_wrappers/include/metrics.h" 36 37 namespace webrtc { 38 39 namespace { 40 41 // Constants from dwmapi.h. 42 const UINT DWM_EC_DISABLECOMPOSITION = 0; 43 const UINT DWM_EC_ENABLECOMPOSITION = 1; 44 45 const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll"; 46 47 } // namespace 48 49 ScreenCapturerWinGdi::ScreenCapturerWinGdi( 50 const DesktopCaptureOptions& options) { 51 if (options.disable_effects()) { 52 // Load dwmapi.dll dynamically since it is not available on XP. 53 if (!dwmapi_library_) 54 dwmapi_library_ = LoadLibraryW(kDwmapiLibraryName); 55 56 if (dwmapi_library_) { 57 composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>( 58 GetProcAddress(dwmapi_library_, "DwmEnableComposition")); 59 } 60 } 61 } 62 63 ScreenCapturerWinGdi::~ScreenCapturerWinGdi() { 64 if (desktop_dc_) 65 ReleaseDC(NULL, desktop_dc_); 66 if (memory_dc_) 67 DeleteDC(memory_dc_); 68 69 // Restore Aero. 70 if (composition_func_) 71 (*composition_func_)(DWM_EC_ENABLECOMPOSITION); 72 73 if (dwmapi_library_) 74 FreeLibrary(dwmapi_library_); 75 } 76 77 void ScreenCapturerWinGdi::SetSharedMemoryFactory( 78 std::unique_ptr<SharedMemoryFactory> shared_memory_factory) { 79 shared_memory_factory_ = std::move(shared_memory_factory); 80 } 81 82 void ScreenCapturerWinGdi::CaptureFrame() { 83 TRACE_EVENT0("webrtc", "ScreenCapturerWinGdi::CaptureFrame"); 84 int64_t capture_start_time_nanos = TimeNanos(); 85 86 queue_.MoveToNextFrame(); 87 if (queue_.current_frame() && queue_.current_frame()->IsShared()) { 88 RTC_DLOG(LS_WARNING) << "Overwriting frame that is still shared."; 89 } 90 91 // Make sure the GDI capture resources are up-to-date. 92 PrepareCaptureResources(); 93 94 if (!CaptureImage()) { 95 RTC_LOG(LS_WARNING) << "Failed to capture screen by GDI."; 96 callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); 97 return; 98 } 99 100 // Emit the current frame. 101 std::unique_ptr<DesktopFrame> frame = queue_.current_frame()->Share(); 102 frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX), 103 GetDeviceCaps(desktop_dc_, LOGPIXELSY))); 104 frame->mutable_updated_region()->SetRect( 105 DesktopRect::MakeSize(frame->size())); 106 107 int capture_time_ms = 108 (TimeNanos() - capture_start_time_nanos) / kNumNanosecsPerMillisec; 109 RTC_HISTOGRAM_COUNTS_1000( 110 "WebRTC.DesktopCapture.Win.ScreenGdiCapturerFrameTime", capture_time_ms); 111 frame->set_capture_time_ms(capture_time_ms); 112 frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinGdi); 113 callback_->OnCaptureResult(Result::SUCCESS, std::move(frame)); 114 } 115 116 bool ScreenCapturerWinGdi::GetSourceList(SourceList* sources) { 117 return GetScreenList(sources); 118 } 119 120 bool ScreenCapturerWinGdi::SelectSource(SourceId id) { 121 std::wstring device_key; 122 bool valid = IsScreenValid(id, &device_key); 123 if (valid) { 124 current_screen_id_ = id; 125 current_device_key_ = device_key; 126 } else { 127 current_screen_id_ = kFullDesktopScreenId; 128 current_device_key_ = std::nullopt; 129 } 130 return valid; 131 } 132 133 void ScreenCapturerWinGdi::Start(Callback* callback) { 134 RTC_DCHECK(!callback_); 135 RTC_DCHECK(callback); 136 RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinGdi); 137 138 callback_ = callback; 139 140 // Vote to disable Aero composited desktop effects while capturing. Windows 141 // will restore Aero automatically if the process exits. This has no effect 142 // under Windows 8 or higher. See crbug.com/124018. 143 if (composition_func_) 144 (*composition_func_)(DWM_EC_DISABLECOMPOSITION); 145 } 146 147 void ScreenCapturerWinGdi::PrepareCaptureResources() { 148 // Switch to the desktop receiving user input if different from the current 149 // one. 150 std::unique_ptr<Desktop> input_desktop(Desktop::GetInputDesktop()); 151 if (input_desktop && !desktop_.IsSame(*input_desktop)) { 152 // Release GDI resources otherwise SetThreadDesktop will fail. 153 if (desktop_dc_) { 154 ReleaseDC(NULL, desktop_dc_); 155 desktop_dc_ = nullptr; 156 } 157 158 if (memory_dc_) { 159 DeleteDC(memory_dc_); 160 memory_dc_ = nullptr; 161 } 162 163 // If SetThreadDesktop() fails, the thread is still assigned a desktop. 164 // So we can continue capture screen bits, just from the wrong desktop. 165 desktop_.SetThreadDesktop(input_desktop.release()); 166 167 // Re-assert our vote to disable Aero. 168 // See crbug.com/124018 and crbug.com/129906. 169 if (composition_func_) { 170 (*composition_func_)(DWM_EC_DISABLECOMPOSITION); 171 } 172 } 173 174 // If the display configurations have changed then recreate GDI resources. 175 if (display_configuration_monitor_.IsChanged(kFullDesktopScreenId)) { 176 if (desktop_dc_) { 177 ReleaseDC(NULL, desktop_dc_); 178 desktop_dc_ = nullptr; 179 } 180 if (memory_dc_) { 181 DeleteDC(memory_dc_); 182 memory_dc_ = nullptr; 183 } 184 } 185 186 if (!desktop_dc_) { 187 RTC_DCHECK(!memory_dc_); 188 189 // Create GDI device contexts to capture from the desktop into memory. 190 desktop_dc_ = GetDC(nullptr); 191 RTC_CHECK(desktop_dc_); 192 memory_dc_ = CreateCompatibleDC(desktop_dc_); 193 RTC_CHECK(memory_dc_); 194 195 // Make sure the frame buffers will be reallocated. 196 queue_.Reset(); 197 } 198 } 199 200 bool ScreenCapturerWinGdi::CaptureImage() { 201 DesktopRect screen_rect = 202 GetScreenRect(current_screen_id_, current_device_key_); 203 if (screen_rect.is_empty()) { 204 RTC_LOG(LS_WARNING) << "Failed to get screen rect."; 205 return false; 206 } 207 208 DesktopSize size = screen_rect.size(); 209 // If the current buffer is from an older generation then allocate a new one. 210 // Note that we can't reallocate other buffers at this point, since the caller 211 // may still be reading from them. 212 if (!queue_.current_frame() || 213 !queue_.current_frame()->size().equals(screen_rect.size())) { 214 RTC_DCHECK(desktop_dc_); 215 RTC_DCHECK(memory_dc_); 216 217 std::unique_ptr<DesktopFrame> buffer = DesktopFrameWin::Create( 218 size, shared_memory_factory_.get(), desktop_dc_); 219 if (!buffer) { 220 RTC_LOG(LS_WARNING) << "Failed to create frame buffer."; 221 return false; 222 } 223 queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(buffer))); 224 } 225 queue_.current_frame()->set_top_left( 226 screen_rect.top_left().subtract(GetFullscreenRect().top_left())); 227 228 // Select the target bitmap into the memory dc and copy the rect from desktop 229 // to memory. 230 DesktopFrameWin* current = static_cast<DesktopFrameWin*>( 231 queue_.current_frame()->GetUnderlyingFrame()); 232 HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap()); 233 if (!previous_object || previous_object == HGDI_ERROR) { 234 RTC_LOG(LS_WARNING) << "Failed to select current bitmap into memery dc."; 235 return false; 236 } 237 238 bool result = (BitBlt(memory_dc_, 0, 0, screen_rect.width(), 239 screen_rect.height(), desktop_dc_, screen_rect.left(), 240 screen_rect.top(), SRCCOPY | CAPTUREBLT) != FALSE); 241 if (!result) { 242 RTC_LOG_GLE(LS_WARNING) << "BitBlt failed"; 243 } 244 245 // Select back the previously selected object to that the device contect 246 // could be destroyed independently of the bitmap if needed. 247 SelectObject(memory_dc_, previous_object); 248 249 return result; 250 } 251 252 } // namespace webrtc