diff options
Diffstat (limited to 'third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h')
-rw-r--r-- | third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h new file mode 100644 index 0000000000..2b1e0ab041 --- /dev/null +++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h @@ -0,0 +1,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_ |