screen_capturer_win_directx.cc (8265B)
1 /* 2 * Copyright (c) 2016 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_directx.h" 12 13 #include <wrl/client.h> 14 15 #include <algorithm> 16 #include <cstddef> 17 #include <cstdint> 18 #include <memory> 19 #include <string> 20 #include <utility> 21 #include <vector> 22 23 #include "modules/desktop_capture/desktop_capture_metrics_helper.h" 24 #include "modules/desktop_capture/desktop_capture_options.h" 25 #include "modules/desktop_capture/desktop_capture_types.h" 26 #include "modules/desktop_capture/desktop_capturer.h" 27 #include "modules/desktop_capture/desktop_frame.h" 28 #include "modules/desktop_capture/screen_capture_frame_queue.h" 29 #include "modules/desktop_capture/shared_memory.h" 30 #include "modules/desktop_capture/win/dxgi_duplicator_controller.h" 31 #include "modules/desktop_capture/win/dxgi_frame.h" 32 #include "modules/desktop_capture/win/screen_capture_utils.h" 33 #include "rtc_base/checks.h" 34 #include "rtc_base/logging.h" 35 #include "rtc_base/time_utils.h" 36 #include "rtc_base/trace_event.h" 37 #include "system_wrappers/include/metrics.h" 38 39 namespace webrtc { 40 41 using Microsoft::WRL::ComPtr; 42 43 // static 44 bool ScreenCapturerWinDirectx::IsSupported() { 45 // Forwards IsSupported() function call to DxgiDuplicatorController. 46 return DxgiDuplicatorController::Instance()->IsSupported(); 47 } 48 49 // static 50 bool ScreenCapturerWinDirectx::RetrieveD3dInfo(D3dInfo* info) { 51 // Forwards SupportedFeatureLevels() function call to 52 // DxgiDuplicatorController. 53 return DxgiDuplicatorController::Instance()->RetrieveD3dInfo(info); 54 } 55 56 // static 57 bool ScreenCapturerWinDirectx::IsCurrentSessionSupported() { 58 return DxgiDuplicatorController::IsCurrentSessionSupported(); 59 } 60 61 // static 62 bool ScreenCapturerWinDirectx::GetScreenListFromDeviceNames( 63 const std::vector<std::string>& device_names, 64 DesktopCapturer::SourceList* screens) { 65 RTC_DCHECK(screens->empty()); 66 67 DesktopCapturer::SourceList gdi_screens; 68 std::vector<std::string> gdi_names; 69 if (!GetScreenList(&gdi_screens, &gdi_names)) { 70 return false; 71 } 72 73 RTC_DCHECK_EQ(gdi_screens.size(), gdi_names.size()); 74 75 ScreenId max_screen_id = -1; 76 for (const DesktopCapturer::Source& screen : gdi_screens) { 77 max_screen_id = std::max(max_screen_id, screen.id); 78 } 79 80 for (const auto& device_name : device_names) { 81 const auto it = std::find(gdi_names.begin(), gdi_names.end(), device_name); 82 if (it == gdi_names.end()) { 83 // devices_names[i] has not been found in gdi_names, so use max_screen_id. 84 max_screen_id++; 85 screens->push_back({max_screen_id}); 86 } else { 87 screens->push_back({gdi_screens[it - gdi_names.begin()]}); 88 } 89 } 90 91 return true; 92 } 93 94 // static 95 int ScreenCapturerWinDirectx::GetIndexFromScreenId( 96 ScreenId id, 97 const std::vector<std::string>& device_names) { 98 DesktopCapturer::SourceList screens; 99 if (!GetScreenListFromDeviceNames(device_names, &screens)) { 100 return -1; 101 } 102 103 RTC_DCHECK_EQ(device_names.size(), screens.size()); 104 105 for (size_t i = 0; i < screens.size(); i++) { 106 if (screens[i].id == id) { 107 return static_cast<int>(i); 108 } 109 } 110 111 return -1; 112 } 113 114 ScreenCapturerWinDirectx::ScreenCapturerWinDirectx() 115 : controller_(DxgiDuplicatorController::Instance()) {} 116 117 ScreenCapturerWinDirectx::ScreenCapturerWinDirectx( 118 const DesktopCaptureOptions& options) 119 : ScreenCapturerWinDirectx() { 120 options_ = options; 121 } 122 123 ScreenCapturerWinDirectx::~ScreenCapturerWinDirectx() = default; 124 125 void ScreenCapturerWinDirectx::Start(Callback* callback) { 126 RTC_DCHECK(!callback_); 127 RTC_DCHECK(callback); 128 RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinDirectx); 129 130 callback_ = callback; 131 } 132 133 void ScreenCapturerWinDirectx::SetSharedMemoryFactory( 134 std::unique_ptr<SharedMemoryFactory> shared_memory_factory) { 135 shared_memory_factory_ = std::move(shared_memory_factory); 136 } 137 138 void ScreenCapturerWinDirectx::CaptureFrame() { 139 RTC_DCHECK(callback_); 140 TRACE_EVENT0("webrtc", "ScreenCapturerWinDirectx::CaptureFrame"); 141 142 int64_t capture_start_time_nanos = TimeNanos(); 143 144 // Note that the [] operator will create the ScreenCaptureFrameQueue if it 145 // doesn't exist, so this is safe. 146 ScreenCaptureFrameQueue<DxgiFrame>& frames = 147 frame_queue_map_[current_screen_id_]; 148 149 frames.MoveToNextFrame(); 150 151 if (!frames.current_frame()) { 152 frames.ReplaceCurrentFrame( 153 std::make_unique<DxgiFrame>(shared_memory_factory_.get())); 154 } 155 156 DxgiDuplicatorController::Result result; 157 if (current_screen_id_ == kFullDesktopScreenId) { 158 result = controller_->Duplicate(frames.current_frame()); 159 } else { 160 result = controller_->DuplicateMonitor(frames.current_frame(), 161 current_screen_id_); 162 } 163 164 using DuplicateResult = DxgiDuplicatorController::Result; 165 if (result != DuplicateResult::SUCCEEDED) { 166 RTC_LOG(LS_ERROR) << "DxgiDuplicatorController failed to capture desktop, " 167 "error code " 168 << DxgiDuplicatorController::ResultName(result); 169 } 170 RTC_HISTOGRAM_ENUMERATION( 171 "WebRTC.DesktopCapture.Win.DirectXCapturerResult", 172 static_cast<int>(result), 173 static_cast<int>(DxgiDuplicatorController::Result::MAX_VALUE)); 174 switch (result) { 175 case DuplicateResult::UNSUPPORTED_SESSION: { 176 RTC_LOG(LS_ERROR) 177 << "Current binary is running on a session not supported " 178 "by DirectX screen capturer."; 179 callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); 180 break; 181 } 182 case DuplicateResult::FRAME_PREPARE_FAILED: { 183 RTC_LOG(LS_ERROR) << "Failed to allocate a new DesktopFrame."; 184 // This usually means we do not have enough memory or SharedMemoryFactory 185 // cannot work correctly. 186 callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); 187 break; 188 } 189 case DuplicateResult::INVALID_MONITOR_ID: { 190 RTC_LOG(LS_ERROR) << "Invalid monitor id " << current_screen_id_; 191 callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); 192 break; 193 } 194 case DuplicateResult::INITIALIZATION_FAILED: 195 case DuplicateResult::DUPLICATION_FAILED: { 196 callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); 197 break; 198 } 199 case DuplicateResult::SUCCEEDED: { 200 std::unique_ptr<DesktopFrame> frame = 201 frames.current_frame()->frame()->Share(); 202 203 int capture_time_ms = 204 (TimeNanos() - capture_start_time_nanos) / kNumNanosecsPerMillisec; 205 RTC_HISTOGRAM_COUNTS_1000( 206 "WebRTC.DesktopCapture.Win.DirectXCapturerFrameTime", 207 capture_time_ms); 208 frame->set_capture_time_ms(capture_time_ms); 209 frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinDirectx); 210 // The DXGI Output Duplicator supports embedding the cursor but it is 211 // only supported on very few display adapters. This switch allows us 212 // to exclude an integrated cursor for all captured frames. 213 if (!options_.prefer_cursor_embedded()) { 214 frame->set_may_contain_cursor(false); 215 } 216 217 // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on 218 // the frame, see WindowCapturerMac::CaptureFrame. 219 220 callback_->OnCaptureResult(Result::SUCCESS, std::move(frame)); 221 break; 222 } 223 } 224 } 225 226 bool ScreenCapturerWinDirectx::GetSourceList(SourceList* sources) { 227 std::vector<std::string> device_names; 228 if (!controller_->GetDeviceNames(&device_names)) { 229 return false; 230 } 231 232 return GetScreenListFromDeviceNames(device_names, sources); 233 } 234 235 bool ScreenCapturerWinDirectx::SelectSource(SourceId id) { 236 if (id == kFullDesktopScreenId) { 237 current_screen_id_ = id; 238 return true; 239 } 240 241 std::vector<std::string> device_names; 242 if (!controller_->GetDeviceNames(&device_names)) { 243 return false; 244 } 245 246 int index; 247 index = GetIndexFromScreenId(id, device_names); 248 if (index == -1) { 249 return false; 250 } 251 252 current_screen_id_ = index; 253 return true; 254 } 255 256 } // namespace webrtc