dxgi_duplicator_controller.h (11833B)
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 #ifndef MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_ 12 #define MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_ 13 14 #include <d3dcommon.h> 15 16 #include <atomic> 17 #include <cstdint> 18 #include <optional> 19 #include <string> 20 #include <vector> 21 22 #include "api/scoped_refptr.h" 23 #include "modules/desktop_capture/desktop_geometry.h" 24 #include "modules/desktop_capture/shared_desktop_frame.h" 25 #include "modules/desktop_capture/win/display_configuration_monitor.h" 26 #include "modules/desktop_capture/win/dxgi_adapter_duplicator.h" 27 #include "modules/desktop_capture/win/dxgi_context.h" 28 #include "modules/desktop_capture/win/dxgi_frame.h" 29 #include "rtc_base/synchronization/mutex.h" 30 #include "rtc_base/system/rtc_export.h" 31 #include "rtc_base/thread_annotations.h" 32 33 namespace webrtc { 34 35 // A controller for all the objects we need to call Windows DirectX capture APIs 36 // It's a singleton because only one IDXGIOutputDuplication instance per monitor 37 // is allowed per application. 38 // 39 // Consumers should create a DxgiDuplicatorController::Context and keep it 40 // throughout their lifetime, and pass it when calling Duplicate(). Consumers 41 // can also call IsSupported() to determine whether the system supports DXGI 42 // duplicator or not. If a previous IsSupported() function call returns true, 43 // but a later Duplicate() returns false, this usually means the display mode is 44 // changing. Consumers should retry after a while. (Typically 50 milliseconds, 45 // but according to hardware performance, this time may vary.) 46 // The underlying DxgiOutputDuplicators may take an additional reference on the 47 // frame passed in to the Duplicate methods so that they can guarantee delivery 48 // of new frames when requested; since if there have been no updates to the 49 // surface, they may be unable to capture a frame. 50 class RTC_EXPORT DxgiDuplicatorController { 51 public: 52 using Context = DxgiFrameContext; 53 54 // A collection of D3d information we are interested in, which may impact 55 // capturer performance or reliability. 56 struct D3dInfo { 57 // Each video adapter has its own D3D_FEATURE_LEVEL, so this structure 58 // contains the minimum and maximium D3D_FEATURE_LEVELs current system 59 // supports. 60 // Both fields can be 0, which is the default value to indicate no valid 61 // D3D_FEATURE_LEVEL has been retrieved from underlying OS APIs. 62 D3D_FEATURE_LEVEL min_feature_level; 63 D3D_FEATURE_LEVEL max_feature_level; 64 65 // TODO(zijiehe): Add more fields, such as manufacturer name, mode, driver 66 // version. 67 }; 68 69 // These values are persisted to logs. Entries should not be renumbered or 70 // reordered and numeric values should never be reused. This enum corresponds 71 // to WebRtcDirectXCapturerResult in tools/metrics/histograms/enums.xml. 72 enum class Result { 73 SUCCEEDED = 0, 74 UNSUPPORTED_SESSION = 1, 75 FRAME_PREPARE_FAILED = 2, 76 INITIALIZATION_FAILED = 3, 77 DUPLICATION_FAILED = 4, 78 INVALID_MONITOR_ID = 5, 79 MAX_VALUE = INVALID_MONITOR_ID 80 }; 81 82 // Converts `result` into user-friendly string representation. The return 83 // value should not be used to identify error types. 84 static std::string ResultName(Result result); 85 86 // Returns the singleton instance of DxgiDuplicatorController. 87 static scoped_refptr<DxgiDuplicatorController> Instance(); 88 89 // See ScreenCapturerWinDirectx::IsCurrentSessionSupported(). 90 static bool IsCurrentSessionSupported(); 91 92 // All the following public functions implicitly call Initialize() function. 93 94 // Detects whether the system supports DXGI based capturer. 95 bool IsSupported(); 96 97 // Returns a copy of D3dInfo composed by last Initialize() function call. This 98 // function always copies the latest information into `info`. But once the 99 // function returns false, the information in `info` may not accurate. 100 bool RetrieveD3dInfo(D3dInfo* info); 101 102 // Captures current screen and writes into `frame`. May retain a reference to 103 // `frame`'s underlying |SharedDesktopFrame|. 104 // TODO(zijiehe): Windows cannot guarantee the frames returned by each 105 // IDXGIOutputDuplication are synchronized. But we are using a totally 106 // different threading model than the way Windows suggested, it's hard to 107 // synchronize them manually. We should find a way to do it. 108 Result Duplicate(DxgiFrame* frame); 109 110 // Captures one monitor and writes into target. `monitor_id` must be >= 0. If 111 // `monitor_id` is greater than the total screen count of all the Duplicators, 112 // this function returns false. May retain a reference to `frame`'s underlying 113 // |SharedDesktopFrame|. 114 Result DuplicateMonitor(DxgiFrame* frame, int monitor_id); 115 116 // Returns dpi of current system. Returns an empty DesktopVector if system 117 // does not support DXGI based capturer. 118 DesktopVector system_dpi(); 119 120 // Returns the count of screens on the system. These screens can be retrieved 121 // by an integer in the range of [0, ScreenCount()). If system does not 122 // support DXGI based capturer, this function returns 0. 123 int ScreenCount(); 124 125 // Returns the device names of all screens on the system in utf8 encoding. 126 // These screens can be retrieved by an integer in the range of 127 // [0, output->size()). If system does not support DXGI based capturer, this 128 // function returns false. 129 bool GetDeviceNames(std::vector<std::string>* output); 130 131 private: 132 // DxgiFrameContext calls private Unregister(Context*) function in Reset(). 133 friend void DxgiFrameContext::Reset(); 134 135 // scoped_refptr<DxgiDuplicatorController> accesses private AddRef() and 136 // Release() functions. 137 friend class scoped_refptr<DxgiDuplicatorController>; 138 139 // A private constructor to ensure consumers to use 140 // DxgiDuplicatorController::Instance(). 141 DxgiDuplicatorController(); 142 143 // Not implemented: The singleton DxgiDuplicatorController instance should not 144 // be deleted. 145 ~DxgiDuplicatorController(); 146 147 // RefCountedInterface implementations. 148 void AddRef(); 149 void Release(); 150 151 // Does the real duplication work. Setting `monitor_id` < 0 to capture entire 152 // screen. This function calls Initialize(). And if the duplication failed, 153 // this function calls Deinitialize() to ensure the Dxgi components can be 154 // reinitialized next time. 155 Result DoDuplicate(DxgiFrame* frame, int monitor_id); 156 157 // Unload all the DXGI components and releases the resources. This function 158 // wraps Deinitialize() with `mutex_`. 159 void Unload(); 160 161 // Unregisters Context from this instance and all DxgiAdapterDuplicator(s) 162 // it owns. 163 void Unregister(const Context* const context); 164 165 // All functions below should be called in `mutex_` locked scope and should be 166 // after a successful Initialize(). 167 168 // If current instance has not been initialized, executes DoInitialize() 169 // function, and returns initialize result. Otherwise directly returns true. 170 // This function may calls Deinitialize() if initialization failed. 171 bool Initialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 172 173 // Does the real initialization work, this function should only be called in 174 // Initialize(). 175 bool DoInitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 176 177 // Clears all COM components referred to by this instance. So next Duplicate() 178 // call will eventually initialize this instance again. 179 void Deinitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 180 181 // A helper function to check whether a Context has been expired. 182 bool ContextExpired(const Context* const context) const 183 RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 184 185 // Updates Context if needed. 186 void Setup(Context* context) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 187 188 bool DoDuplicateUnlocked(Context* context, 189 int monitor_id, 190 SharedDesktopFrame* target) 191 RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 192 193 // Captures all monitors. 194 bool DoDuplicateAll(Context* context, SharedDesktopFrame* target) 195 RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 196 197 // Captures one monitor. 198 bool DoDuplicateOne(Context* context, 199 int monitor_id, 200 SharedDesktopFrame* target) 201 RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 202 203 // When monitor_id is kFullDesktopScreenId, meaning capturing all screens, 204 // the minimum GetNumFramesCaptured(int monitor_id) returned by duplicators_. 205 int64_t GetNumFramesCaptured(int monitor_id) const 206 RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 207 208 // Returns a DesktopSize to cover entire `desktop_rect_`. 209 DesktopSize desktop_size() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 210 211 // Returns the device scale factor of one screen. `monitor_id` should be >= 0. 212 // If system does not support DXGI based capturer, or `monitor_id` is greater 213 // than the total screen count of all the Duplicators, this function returns 214 // std::nullopt. 215 std::optional<float> GetDeviceScaleFactor(int monitor_id) const 216 RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 217 218 // Returns the size of one screen. `id` should be >= 0. If system does not 219 // support DXGI based capturer, or `id` is greater than the total screen count 220 // of all the Duplicators, this function returns an empty DesktopRect. 221 DesktopRect ScreenRect(int id) const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 222 223 int ScreenCountUnlocked() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 224 225 void GetDeviceNamesUnlocked(std::vector<std::string>* output) const 226 RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 227 228 // Returns the desktop size of the selected screen `monitor_id`. Setting 229 // `monitor_id` < 0 to return the entire screen size. 230 DesktopSize SelectedDesktopSize(int monitor_id) const 231 RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 232 233 // Retries DoDuplicateAll() for several times until GetNumFramesCaptured() is 234 // large enough. Returns false if DoDuplicateAll() returns false, or 235 // GetNumFramesCaptured() has never reached the requirement. 236 // According to http://crbug.com/682112, dxgi capturer returns a black frame 237 // during first several capture attempts. 238 bool EnsureFrameCaptured(Context* context, 239 int monitor_id, 240 SharedDesktopFrame* target) 241 RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 242 243 // Moves `desktop_rect_` and all underlying `duplicators_`, putting top left 244 // corner of the desktop at (0, 0). This is necessary because DXGI_OUTPUT_DESC 245 // may return negative coordinates. Called from DoInitialize() after all 246 // DxgiAdapterDuplicator and DxgiOutputDuplicator instances are initialized. 247 void TranslateRect() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_); 248 249 // The count of references which are now "living". 250 std::atomic_int refcount_; 251 252 // This lock must be locked whenever accessing any of the following objects. 253 Mutex mutex_; 254 255 // A self-incremented integer to compare with the one in Context. It ensures 256 // a Context instance is always initialized after DxgiDuplicatorController. 257 int identity_ RTC_GUARDED_BY(mutex_) = 0; 258 DesktopRect desktop_rect_ RTC_GUARDED_BY(mutex_); 259 DesktopVector system_dpi_ RTC_GUARDED_BY(mutex_); 260 std::vector<DxgiAdapterDuplicator> duplicators_ RTC_GUARDED_BY(mutex_); 261 D3dInfo d3d_info_ RTC_GUARDED_BY(mutex_); 262 DisplayConfigurationMonitor display_configuration_monitor_ 263 RTC_GUARDED_BY(mutex_); 264 // A number to indicate how many successful duplications have been performed. 265 uint32_t succeeded_duplications_ RTC_GUARDED_BY(mutex_) = 0; 266 }; 267 268 } // namespace webrtc 269 270 #endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_