• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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