mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-12 21:30:45 +01:00

leaving a compatible alias. Bug: webrtc:15622 Change-Id: Ie25d87fa372cc71eaf52882454f4dd24c7c33789 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/325462 Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Commit-Queue: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#41072}
257 lines
11 KiB
C++
257 lines
11 KiB
C++
/*
|
|
* 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 webrtc::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_
|