aboutsummaryrefslogtreecommitdiff
path: root/modules/desktop_capture/win/dxgi_duplicator_controller.h
blob: 2b1e0ab041f8a34e9e9342a70d530bcb39ee1c3a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/*
 *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#ifndef MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_
#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_

#include <d3dcommon.h>

#include <atomic>
#include <string>
#include <vector>

#include "api/scoped_refptr.h"
#include "modules/desktop_capture/desktop_geometry.h"
#include "modules/desktop_capture/shared_desktop_frame.h"
#include "modules/desktop_capture/win/d3d_device.h"
#include "modules/desktop_capture/win/display_configuration_monitor.h"
#include "modules/desktop_capture/win/dxgi_adapter_duplicator.h"
#include "modules/desktop_capture/win/dxgi_context.h"
#include "modules/desktop_capture/win/dxgi_frame.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/system/rtc_export.h"

namespace webrtc {

// A controller for all the objects we need to call Windows DirectX capture APIs
// It's a singleton because only one IDXGIOutputDuplication instance per monitor
// is allowed per application.
//
// Consumers should create a DxgiDuplicatorController::Context and keep it
// throughout their lifetime, and pass it when calling Duplicate(). Consumers
// can also call IsSupported() to determine whether the system supports DXGI
// duplicator or not. If a previous IsSupported() function call returns true,
// but a later Duplicate() returns false, this usually means the display mode is
// changing. Consumers should retry after a while. (Typically 50 milliseconds,
// but according to hardware performance, this time may vary.)
// The underlying DxgiOutputDuplicators may take an additional reference on the
// frame passed in to the Duplicate methods so that they can guarantee delivery
// of new frames when requested; since if there have been no updates to the
// surface, they may be unable to capture a frame.
class RTC_EXPORT DxgiDuplicatorController {
 public:
  using Context = DxgiFrameContext;

  // A collection of D3d information we are interested in, which may impact
  // capturer performance or reliability.
  struct D3dInfo {
    // Each video adapter has its own D3D_FEATURE_LEVEL, so this structure
    // contains the minimum and maximium D3D_FEATURE_LEVELs current system
    // supports.
    // Both fields can be 0, which is the default value to indicate no valid
    // D3D_FEATURE_LEVEL has been retrieved from underlying OS APIs.
    D3D_FEATURE_LEVEL min_feature_level;
    D3D_FEATURE_LEVEL max_feature_level;

    // TODO(zijiehe): Add more fields, such as manufacturer name, mode, driver
    // version.
  };

  // These values are persisted to logs. Entries should not be renumbered or
  // reordered and numeric values should never be reused. This enum corresponds
  // to WebRtcDirectXCapturerResult in tools/metrics/histograms/enums.xml.
  enum class Result {
    SUCCEEDED = 0,
    UNSUPPORTED_SESSION = 1,
    FRAME_PREPARE_FAILED = 2,
    INITIALIZATION_FAILED = 3,
    DUPLICATION_FAILED = 4,
    INVALID_MONITOR_ID = 5,
    MAX_VALUE = INVALID_MONITOR_ID
  };

  // Converts `result` into user-friendly string representation. The return
  // value should not be used to identify error types.
  static std::string ResultName(Result result);

  // Returns the singleton instance of DxgiDuplicatorController.
  static rtc::scoped_refptr<DxgiDuplicatorController> Instance();

  // See ScreenCapturerWinDirectx::IsCurrentSessionSupported().
  static bool IsCurrentSessionSupported();

  // All the following public functions implicitly call Initialize() function.

  // Detects whether the system supports DXGI based capturer.
  bool IsSupported();

  // Returns a copy of D3dInfo composed by last Initialize() function call. This
  // function always copies the latest information into `info`. But once the
  // function returns false, the information in `info` may not accurate.
  bool RetrieveD3dInfo(D3dInfo* info);

  // Captures current screen and writes into `frame`. May retain a reference to
  // `frame`'s underlying |SharedDesktopFrame|.
  // TODO(zijiehe): Windows cannot guarantee the frames returned by each
  // IDXGIOutputDuplication are synchronized. But we are using a totally
  // different threading model than the way Windows suggested, it's hard to
  // synchronize them manually. We should find a way to do it.
  Result Duplicate(DxgiFrame* frame);

  // Captures one monitor and writes into target. `monitor_id` must be >= 0. If
  // `monitor_id` is greater than the total screen count of all the Duplicators,
  // this function returns false. May retain a reference to `frame`'s underlying
  // |SharedDesktopFrame|.
  Result DuplicateMonitor(DxgiFrame* frame, int monitor_id);

  // Returns dpi of current system. Returns an empty DesktopVector if system
  // does not support DXGI based capturer.
  DesktopVector system_dpi();

  // Returns the count of screens on the system. These screens can be retrieved
  // by an integer in the range of [0, ScreenCount()). If system does not
  // support DXGI based capturer, this function returns 0.
  int ScreenCount();

  // Returns the device names of all screens on the system in utf8 encoding.
  // These screens can be retrieved by an integer in the range of
  // [0, output->size()). If system does not support DXGI based capturer, this
  // function returns false.
  bool GetDeviceNames(std::vector<std::string>* output);

 private:
  // DxgiFrameContext calls private Unregister(Context*) function in Reset().
  friend void DxgiFrameContext::Reset();

  // scoped_refptr<DxgiDuplicatorController> accesses private AddRef() and
  // Release() functions.
  friend class rtc::scoped_refptr<DxgiDuplicatorController>;

  // A private constructor to ensure consumers to use
  // DxgiDuplicatorController::Instance().
  DxgiDuplicatorController();

  // Not implemented: The singleton DxgiDuplicatorController instance should not
  // be deleted.
  ~DxgiDuplicatorController();

  // RefCountedInterface implementations.
  void AddRef();
  void Release();

  // Does the real duplication work. Setting `monitor_id` < 0 to capture entire
  // screen. This function calls Initialize(). And if the duplication failed,
  // this function calls Deinitialize() to ensure the Dxgi components can be
  // reinitialized next time.
  Result DoDuplicate(DxgiFrame* frame, int monitor_id);

  // Unload all the DXGI components and releases the resources. This function
  // wraps Deinitialize() with `mutex_`.
  void Unload();

  // Unregisters Context from this instance and all DxgiAdapterDuplicator(s)
  // it owns.
  void Unregister(const Context* const context);

  // All functions below should be called in `mutex_` locked scope and should be
  // after a successful Initialize().

  // If current instance has not been initialized, executes DoInitialize()
  // function, and returns initialize result. Otherwise directly returns true.
  // This function may calls Deinitialize() if initialization failed.
  bool Initialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // Does the real initialization work, this function should only be called in
  // Initialize().
  bool DoInitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // Clears all COM components referred to by this instance. So next Duplicate()
  // call will eventually initialize this instance again.
  void Deinitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // A helper function to check whether a Context has been expired.
  bool ContextExpired(const Context* const context) const
      RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // Updates Context if needed.
  void Setup(Context* context) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  bool DoDuplicateUnlocked(Context* context,
                           int monitor_id,
                           SharedDesktopFrame* target)
      RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // Captures all monitors.
  bool DoDuplicateAll(Context* context, SharedDesktopFrame* target)
      RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // Captures one monitor.
  bool DoDuplicateOne(Context* context,
                      int monitor_id,
                      SharedDesktopFrame* target)
      RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // The minimum GetNumFramesCaptured() returned by `duplicators_`.
  int64_t GetNumFramesCaptured() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // Returns a DesktopSize to cover entire `desktop_rect_`.
  DesktopSize desktop_size() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // Returns the size of one screen. `id` should be >= 0. If system does not
  // support DXGI based capturer, or `id` is greater than the total screen count
  // of all the Duplicators, this function returns an empty DesktopRect.
  DesktopRect ScreenRect(int id) const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  int ScreenCountUnlocked() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  void GetDeviceNamesUnlocked(std::vector<std::string>* output) const
      RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // Returns the desktop size of the selected screen `monitor_id`. Setting
  // `monitor_id` < 0 to return the entire screen size.
  DesktopSize SelectedDesktopSize(int monitor_id) const
      RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // Retries DoDuplicateAll() for several times until GetNumFramesCaptured() is
  // large enough. Returns false if DoDuplicateAll() returns false, or
  // GetNumFramesCaptured() has never reached the requirement.
  // According to http://crbug.com/682112, dxgi capturer returns a black frame
  // during first several capture attempts.
  bool EnsureFrameCaptured(Context* context, SharedDesktopFrame* target)
      RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // Moves `desktop_rect_` and all underlying `duplicators_`, putting top left
  // corner of the desktop at (0, 0). This is necessary because DXGI_OUTPUT_DESC
  // may return negative coordinates. Called from DoInitialize() after all
  // DxgiAdapterDuplicator and DxgiOutputDuplicator instances are initialized.
  void TranslateRect() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);

  // The count of references which are now "living".
  std::atomic_int refcount_;

  // This lock must be locked whenever accessing any of the following objects.
  Mutex mutex_;

  // A self-incremented integer to compare with the one in Context. It ensures
  // a Context instance is always initialized after DxgiDuplicatorController.
  int identity_ RTC_GUARDED_BY(mutex_) = 0;
  DesktopRect desktop_rect_ RTC_GUARDED_BY(mutex_);
  DesktopVector system_dpi_ RTC_GUARDED_BY(mutex_);
  std::vector<DxgiAdapterDuplicator> duplicators_ RTC_GUARDED_BY(mutex_);
  D3dInfo d3d_info_ RTC_GUARDED_BY(mutex_);
  DisplayConfigurationMonitor display_configuration_monitor_
      RTC_GUARDED_BY(mutex_);
  // A number to indicate how many successful duplications have been performed.
  uint32_t succeeded_duplications_ RTC_GUARDED_BY(mutex_) = 0;
};

}  // namespace webrtc

#endif  // MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_