tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

dxgi_duplicator_controller.h (11833B)


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