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