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