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