diff --git a/webrtc/modules/desktop_capture/BUILD.gn b/webrtc/modules/desktop_capture/BUILD.gn index f8d5293405..35ca3a447f 100644 --- a/webrtc/modules/desktop_capture/BUILD.gn +++ b/webrtc/modules/desktop_capture/BUILD.gn @@ -203,8 +203,12 @@ rtc_static_library("desktop_capture") { "win/desktop.h", "win/dxgi_adapter_duplicator.cc", "win/dxgi_adapter_duplicator.h", + "win/dxgi_context.cc", + "win/dxgi_context.h", "win/dxgi_duplicator_controller.cc", "win/dxgi_duplicator_controller.h", + "win/dxgi_frame.cc", + "win/dxgi_frame.h", "win/dxgi_output_duplicator.cc", "win/dxgi_output_duplicator.h", "win/dxgi_texture.cc", diff --git a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc index 52bf2a194b..caef1c2ca7 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc @@ -31,10 +31,6 @@ bool IsValidRect(const RECT& rect) { } // namespace -DxgiAdapterDuplicator::Context::Context() = default; -DxgiAdapterDuplicator::Context::Context(const Context& other) = default; -DxgiAdapterDuplicator::Context::~Context() = default; - DxgiAdapterDuplicator::DxgiAdapterDuplicator(const D3dDevice& device) : device_(device) {} DxgiAdapterDuplicator::DxgiAdapterDuplicator(DxgiAdapterDuplicator&&) = default; diff --git a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h index a1ef0438c6..4aee8b85b0 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h +++ b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h @@ -16,9 +16,9 @@ #include #include "webrtc/modules/desktop_capture/desktop_geometry.h" -#include "webrtc/modules/desktop_capture/desktop_region.h" #include "webrtc/modules/desktop_capture/shared_desktop_frame.h" #include "webrtc/modules/desktop_capture/win/d3d_device.h" +#include "webrtc/modules/desktop_capture/win/dxgi_context.h" #include "webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h" namespace webrtc { @@ -27,15 +27,7 @@ namespace webrtc { // single video card. class DxgiAdapterDuplicator { public: - struct Context { - Context(); - Context(const Context& other); - ~Context(); - - // Child DxgiOutputDuplicator::Context belongs to this - // DxgiAdapterDuplicator::Context. - std::vector contexts; - }; + using Context = DxgiAdapterContext; // Creates an instance of DxgiAdapterDuplicator from a D3dDevice. Only // DxgiDuplicatorController can create an instance. diff --git a/webrtc/modules/desktop_capture/win/dxgi_context.cc b/webrtc/modules/desktop_capture/win/dxgi_context.cc new file mode 100644 index 0000000000..dffefc404f --- /dev/null +++ b/webrtc/modules/desktop_capture/win/dxgi_context.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017 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. + */ + +#include "webrtc/modules/desktop_capture/win/dxgi_context.h" +#include "webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h" + +namespace webrtc { + +DxgiAdapterContext::DxgiAdapterContext() = default; +DxgiAdapterContext::DxgiAdapterContext( + const DxgiAdapterContext& context) = default; +DxgiAdapterContext::~DxgiAdapterContext() = default; + +DxgiFrameContext::DxgiFrameContext() = default; + +DxgiFrameContext::~DxgiFrameContext() { + DxgiDuplicatorController::Instance()->Unregister(this); +} + +void DxgiFrameContext::Reset() { + controller_id = 0; +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/dxgi_context.h b/webrtc/modules/desktop_capture/win/dxgi_context.h new file mode 100644 index 0000000000..323ea7cad3 --- /dev/null +++ b/webrtc/modules/desktop_capture/win/dxgi_context.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 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 WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_CONTEXT_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_CONTEXT_H_ + +#include +#include "webrtc/modules/desktop_capture/desktop_region.h" + +namespace webrtc { + +// A DxgiOutputContext stores the status of a single DxgiFrame of +// DxgiOutputDuplicator. +struct DxgiOutputContext final { + // The updated region DxgiOutputDuplicator::DetectUpdatedRegion() output + // during last Duplicate() function call. It's always relative to the (0, 0). + DesktopRegion updated_region; +}; + +// A DxgiAdapterContext stores the status of a single DxgiFrame of +// DxgiAdapterDuplicator. +struct DxgiAdapterContext final { + DxgiAdapterContext(); + DxgiAdapterContext(const DxgiAdapterContext& other); + ~DxgiAdapterContext(); + + // Child DxgiOutputContext belongs to this AdapterContext. + std::vector contexts; +}; + +// A DxgiFrameContext stores the status of a single DxgiFrame of +// DxgiDuplicatorController. +struct DxgiFrameContext final { + public: + DxgiFrameContext(); + // Unregister this Context instance from DxgiDuplicatorController during + // destructing. + ~DxgiFrameContext(); + + // Reset current Context, so it will be reinitialized next time. + void Reset(); + + // A Context will have an exactly same |controller_id| as + // DxgiDuplicatorController, to ensure it has been correctly setted up after + // each DxgiDuplicatorController::Initialize(). + int controller_id = 0; + + // Child DxgiAdapterContext belongs to this DxgiFrameContext. + std::vector contexts; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_CONTEXT_H_ diff --git a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc index a5f09f0040..12876c2c89 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc @@ -18,21 +18,12 @@ #include "webrtc/base/checks.h" #include "webrtc/base/timeutils.h" #include "webrtc/modules/desktop_capture/desktop_capture_types.h" +#include "webrtc/modules/desktop_capture/win/dxgi_frame.h" #include "webrtc/modules/desktop_capture/win/screen_capture_utils.h" #include "webrtc/system_wrappers/include/sleep.h" namespace webrtc { -DxgiDuplicatorController::Context::Context() = default; - -DxgiDuplicatorController::Context::~Context() { - DxgiDuplicatorController::Instance()->Unregister(this); -} - -void DxgiDuplicatorController::Context::Reset() { - identity_ = 0; -} - // static DxgiDuplicatorController* DxgiDuplicatorController::Instance() { // The static instance won't be deleted to ensure it can be used by other @@ -53,11 +44,6 @@ bool DxgiDuplicatorController::IsSupported() { return Initialize(); } -void DxgiDuplicatorController::Reset() { - rtc::CritScope lock(&lock_); - Deinitialize(); -} - bool DxgiDuplicatorController::RetrieveD3dInfo(D3dInfo* info) { rtc::CritScope lock(&lock_); if (!Initialize()) { @@ -67,6 +53,17 @@ bool DxgiDuplicatorController::RetrieveD3dInfo(D3dInfo* info) { return true; } +DxgiDuplicatorController::Result +DxgiDuplicatorController::Duplicate(DxgiFrame* frame) { + return DoDuplicate(frame, -1); +} + +DxgiDuplicatorController::Result +DxgiDuplicatorController::DuplicateMonitor(DxgiFrame* frame, int monitor_id) { + RTC_DCHECK_GE(monitor_id, 0); + return DoDuplicate(frame, monitor_id); +} + DesktopVector DxgiDuplicatorController::dpi() { rtc::CritScope lock(&lock_); if (Initialize()) { @@ -75,38 +72,61 @@ DesktopVector DxgiDuplicatorController::dpi() { return DesktopVector(); } -DesktopRect DxgiDuplicatorController::desktop_rect() { - rtc::CritScope lock(&lock_); - if (Initialize()) { - return desktop_rect_; - } - return DesktopRect(); -} - -DesktopSize DxgiDuplicatorController::desktop_size() { - DesktopRect rect = desktop_rect(); - return DesktopSize(rect.right(), rect.bottom()); -} - -DesktopRect DxgiDuplicatorController::ScreenRect(int id) { - RTC_DCHECK(id >= 0); - rtc::CritScope lock(&lock_); - if (!Initialize()) { - return DesktopRect(); - } - for (size_t i = 0; i < duplicators_.size(); i++) { - if (id >= duplicators_[i].screen_count()) { - id -= duplicators_[i].screen_count(); - } else { - return duplicators_[i].ScreenRect(id); - } - } - return DesktopRect(); -} - int DxgiDuplicatorController::ScreenCount() { rtc::CritScope lock(&lock_); - return ScreenCountUnlocked(); + if (Initialize()) { + return ScreenCountUnlocked(); + } + return 0; +} + +DxgiDuplicatorController::Result +DxgiDuplicatorController::DoDuplicate(DxgiFrame* frame, int monitor_id) { + RTC_DCHECK(frame); + rtc::CritScope lock(&lock_); + + // The dxgi components and APIs do not update the screen resolution without + // a reinitialization. So we use the GetDC() function to retrieve the screen + // resolution to decide whether dxgi components need to be reinitialized. + // If the screen resolution changed, it's very likely the next Duplicate() + // function call will fail because of a missing monitor or the frame size is + // not enough to store the output. So we reinitialize dxgi components in-place + // to avoid a capture failure. + // But there is no guarantee GetDC() function returns the same resolution as + // dxgi APIs, we still rely on dxgi components to return the output frame + // size. + // TODO(zijiehe): Confirm whether IDXGIOutput::GetDesc() and + // IDXGIOutputDuplication::GetDesc() can detect the resolution change without + // reinitialization. + if (resolution_change_detector_.IsChanged( + GetScreenRect(kFullDesktopScreenId, std::wstring()).size())) { + Deinitialize(); + } + + if (!Initialize()) { + // Cannot initialize COM components now, display mode may be changing. + return Result::INITIALIZATION_FAILED; + } + + if (!frame->Prepare(SelectedDesktopSize(monitor_id), monitor_id)) { + return Result::FRAME_PREPARE_FAILED; + } + + frame->frame()->mutable_updated_region()->Clear(); + + if (DoDuplicateUnlocked(frame->context(), monitor_id, frame->frame())) { + return Result::SUCCEEDED; + } + if (monitor_id >= ScreenCountUnlocked()) { + // It's a user error to provide a |monitor_id| larger than screen count. We + // do not need to deinitialize. + return Result::INVALID_MONITOR_ID; + } + + // If the |monitor_id| is valid, but DoDuplicateUnlocked() failed, something + // must be wrong from capturer APIs. We should Deinitialize(). + Deinitialize(); + return Result::DUPLICATION_FAILED; } void DxgiDuplicatorController::Unregister(const Context* const context) { @@ -117,7 +137,7 @@ void DxgiDuplicatorController::Unregister(const Context* const context) { return; } for (size_t i = 0; i < duplicators_.size(); i++) { - duplicators_[i].Unregister(&context->contexts_[i]); + duplicators_[i].Unregister(&context->contexts[i]); } } @@ -194,66 +214,26 @@ void DxgiDuplicatorController::Deinitialize() { bool DxgiDuplicatorController::ContextExpired( const Context* const context) const { - return context->identity_ != identity_ || - context->contexts_.size() != duplicators_.size(); + RTC_DCHECK(context); + return context->controller_id != identity_ || + context->contexts.size() != duplicators_.size(); } void DxgiDuplicatorController::Setup(Context* context) { if (ContextExpired(context)) { - context->contexts_.clear(); - context->contexts_.resize(duplicators_.size()); + RTC_DCHECK(context); + context->contexts.clear(); + context->contexts.resize(duplicators_.size()); for (size_t i = 0; i < duplicators_.size(); i++) { - duplicators_[i].Setup(&context->contexts_[i]); + duplicators_[i].Setup(&context->contexts[i]); } - context->identity_ = identity_; + context->controller_id = identity_; } } -bool DxgiDuplicatorController::Duplicate(Context* context, - SharedDesktopFrame* target) { - return DoDuplicate(context, -1, target); -} - -bool DxgiDuplicatorController::DuplicateMonitor(Context* context, - int monitor_id, - SharedDesktopFrame* target) { - RTC_DCHECK_GE(monitor_id, 0); - return DoDuplicate(context, monitor_id, target); -} - -bool DxgiDuplicatorController::DoDuplicate(Context* context, - int monitor_id, - SharedDesktopFrame* target) { - RTC_DCHECK(target); - target->mutable_updated_region()->Clear(); - rtc::CritScope lock(&lock_); - if (DoDuplicateUnlocked(context, monitor_id, target)) { - return true; - } - if (monitor_id < ScreenCountUnlocked()) { - // It's a user error to provide a |monitor_id| larger than screen count. We - // do not need to deinitialize. - Deinitialize(); - } - return false; -} - bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context, int monitor_id, SharedDesktopFrame* target) { - if (!Initialize()) { - // Cannot initialize COM components now, display mode may be changing. - return false; - } - - if (resolution_change_detector_.IsChanged( - GetScreenRect(kFullDesktopScreenId, std::wstring()).size())) { - // Resolution of entire screen has been changed, which usually means a new - // monitor has been attached or one has been removed. The simplest way is to - // Deinitialize() and returns false to indicate downstream components. - return false; - } - Setup(context); if (!EnsureFrameCaptured(context, target)) { @@ -279,7 +259,7 @@ bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context, bool DxgiDuplicatorController::DoDuplicateAll(Context* context, SharedDesktopFrame* target) { for (size_t i = 0; i < duplicators_.size(); i++) { - if (!duplicators_[i].Duplicate(&context->contexts_[i], target)) { + if (!duplicators_[i].Duplicate(&context->contexts[i], target)) { return false; } } @@ -290,12 +270,12 @@ bool DxgiDuplicatorController::DoDuplicateOne(Context* context, int monitor_id, SharedDesktopFrame* target) { RTC_DCHECK(monitor_id >= 0); - for (size_t i = 0; i < duplicators_.size() && i < context->contexts_.size(); + for (size_t i = 0; i < duplicators_.size() && i < context->contexts.size(); i++) { if (monitor_id >= duplicators_[i].screen_count()) { monitor_id -= duplicators_[i].screen_count(); } else { - if (duplicators_[i].DuplicateMonitor(&context->contexts_[i], monitor_id, + if (duplicators_[i].DuplicateMonitor(&context->contexts[i], monitor_id, target)) { return true; } @@ -314,10 +294,23 @@ int64_t DxgiDuplicatorController::GetNumFramesCaptured() const { return min; } -int DxgiDuplicatorController::ScreenCountUnlocked() { - if (!Initialize()) { - return 0; +DesktopSize DxgiDuplicatorController::desktop_size() const { + return DesktopSize(desktop_rect_.right(), desktop_rect_.bottom()); +} + +DesktopRect DxgiDuplicatorController::ScreenRect(int id) const { + RTC_DCHECK(id >= 0); + for (size_t i = 0; i < duplicators_.size(); i++) { + if (id >= duplicators_[i].screen_count()) { + id -= duplicators_[i].screen_count(); + } else { + return duplicators_[i].ScreenRect(id); + } } + return DesktopRect(); +} + +int DxgiDuplicatorController::ScreenCountUnlocked() const { int result = 0; for (auto& duplicator : duplicators_) { result += duplicator.screen_count(); @@ -325,6 +318,15 @@ int DxgiDuplicatorController::ScreenCountUnlocked() { return result; } +DesktopSize DxgiDuplicatorController::SelectedDesktopSize( + int monitor_id) const { + if (monitor_id < 0) { + return desktop_size(); + } + + return ScreenRect(monitor_id).size(); +} + bool DxgiDuplicatorController::EnsureFrameCaptured(Context* context, SharedDesktopFrame* target) { // On a modern system, the FPS / monitor refresh rate is usually larger than diff --git a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h index 310eff02c9..364690bcb9 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h +++ b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h @@ -18,11 +18,12 @@ #include "webrtc/base/criticalsection.h" #include "webrtc/modules/desktop_capture/desktop_geometry.h" -#include "webrtc/modules/desktop_capture/desktop_region.h" #include "webrtc/modules/desktop_capture/resolution_change_detector.h" #include "webrtc/modules/desktop_capture/shared_desktop_frame.h" #include "webrtc/modules/desktop_capture/win/d3d_device.h" #include "webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h" +#include "webrtc/modules/desktop_capture/win/dxgi_context.h" +#include "webrtc/modules/desktop_capture/win/dxgi_frame.h" namespace webrtc { @@ -39,29 +40,7 @@ namespace webrtc { // but according to hardware performance, this time may vary.) class DxgiDuplicatorController { public: - // A context to store the status of a single consumer of - // DxgiDuplicatorController. - class Context { - public: - Context(); - // Unregister this Context instance from all Dxgi duplicators during - // destructing. - ~Context(); - - // Reset current Context, so it will be reinitialized next time. - void Reset(); - - private: - friend class DxgiDuplicatorController; - - // A Context will have an exactly same |identity_| as - // DxgiDuplicatorController, to ensure it has been correctly setted up after - // each DxgiDuplicatorController::Initialize(). - int identity_ = 0; - - // Child DxgiAdapterDuplicator::Context belongs to this Context. - std::vector contexts_; - }; + using Context = DxgiFrameContext; // A collection of D3d information we are interested on, which may impact // capturer performance or reliability. @@ -78,87 +57,80 @@ class DxgiDuplicatorController { // version. }; + enum class Result { + SUCCEEDED, + FRAME_PREPARE_FAILED, + INITIALIZATION_FAILED, + DUPLICATION_FAILED, + INVALID_MONITOR_ID, + }; + // Returns the singleton instance of DxgiDuplicatorController. static DxgiDuplicatorController* Instance(); // Destructs current instance. We need to make sure COM components and their - // containers are destructed in correct order. + // containers are destructed in correct order. This function calls + // Deinitialize() to do the real work. ~DxgiDuplicatorController(); - // All the following functions implicitly call Initialize() function if - // current instance has not been initialized. + // All the following public functions implicitly call Initialize() function. // Detects whether the system supports DXGI based capturer. bool IsSupported(); - // Calls Deinitialize() function with lock. Consumers can call this function - // to force the DxgiDuplicatorController to be reinitialized to avoid an - // expected failure in next Duplicate() call. - void Reset(); - // Returns a copy of D3dInfo composed by last Initialize() function call. bool RetrieveD3dInfo(D3dInfo* info); - // Captures current screen and writes into target. Since we are using double - // buffering, |last_frame|.updated_region() is used to represent the not - // updated regions in current |target| frame, which should also be copied this - // time. + // Captures current screen and writes into |frame|. // 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. - bool Duplicate(Context* context, SharedDesktopFrame* target); + Result Duplicate(DxgiFrame* frame); // Captures one monitor and writes into target. |monitor_id| should >= 0. If // |monitor_id| is greater than the total screen count of all the Duplicators, // this function returns false. - bool DuplicateMonitor(Context* context, - int monitor_id, - SharedDesktopFrame* target); + 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 dpi(); - // Returns entire desktop size. Returns an empty DesktopRect if system does - // not support DXGI based capturer. - DesktopRect desktop_rect(); - - // Returns a DesktopSize to cover entire desktop_rect. This may be different - // than desktop_rect().size(), since top-left screen does not need to start - // from (0, 0). - DesktopSize desktop_size(); - - // Returns the size of one screen. |monitor_id| should be >= 0. If system does - // not support DXGI based capturer, or |monitor_id| is greater than the total - // screen count of all the Duplicators, this function returns an empty - // DesktopRect. - DesktopRect ScreenRect(int id); - // 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(); private: - // Context calls private Unregister(Context*) function during + // DxgiFrameContext calls private Unregister(Context*) function during // destructing. - friend class Context; + friend DxgiFrameContext::~DxgiFrameContext(); // A private constructor to ensure consumers to use // DxgiDuplicatorController::Instance(). DxgiDuplicatorController(); + // 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); + // Unregisters Context from this instance and all DxgiAdapterDuplicator(s) // it owns. void Unregister(const Context* const context); - // All functions below should be called in |lock_| locked scope. + // All functions below should be called in |lock_| locked scope and should be + // after a successful Initialize(). - // If current instance has not been initialized, executes DoInitialize + // 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(); + // Does the real initialization work, this function should only be called in + // Initialize(). bool DoInitialize(); // Clears all COM components referred by this instance. So next Duplicate() @@ -171,11 +143,6 @@ class DxgiDuplicatorController { // Updates Context if needed. void Setup(Context* context); - // Does the real duplication work. |monitor_id < 0| to capture entire screen. - bool DoDuplicate(Context* context, - int monitor_id, - SharedDesktopFrame* target); - bool DoDuplicateUnlocked(Context* context, int monitor_id, SharedDesktopFrame* target); @@ -191,7 +158,21 @@ class DxgiDuplicatorController { // The minimum GetNumFramesCaptured() returned by |duplicators_|. int64_t GetNumFramesCaptured() const; - int ScreenCountUnlocked(); + // Returns a DesktopSize to cover entire desktop_rect. This may be different + // than desktop_rect().size(), since top-left of the screen does not need to + // be started from (0, 0). + DesktopSize desktop_size() const; + + // 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; + + int ScreenCountUnlocked() const; + + // 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; // Retries DoDuplicateAll() for several times until GetNumFramesCaptured() is // large enough. Returns false if DoDuplicateAll() returns false, or @@ -203,8 +184,8 @@ class DxgiDuplicatorController { // This lock must be locked whenever accessing any of the following objects. rtc::CriticalSection lock_; - // A self-incremented integer to compare with the one in Context, to - // ensure a Context has been initialized after DxgiDuplicatorController. + // A self-incremented integer to compare with the one in Context. It ensures + // a Context instance is always initialized after DxgiDuplicatorController. int identity_ = 0; DesktopRect desktop_rect_; DesktopVector dpi_; diff --git a/webrtc/modules/desktop_capture/win/dxgi_frame.cc b/webrtc/modules/desktop_capture/win/dxgi_frame.cc new file mode 100644 index 0000000000..0ba678dd0a --- /dev/null +++ b/webrtc/modules/desktop_capture/win/dxgi_frame.cc @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 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. + */ + +#include "webrtc/modules/desktop_capture/win/dxgi_frame.h" + +#include + +#include "webrtc/base/checks.h" +#include "webrtc/modules/desktop_capture/desktop_frame.h" +#include "webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h" + +namespace webrtc { + +DxgiFrame::DxgiFrame(SharedMemoryFactory* factory) + : factory_(factory) {} + +DxgiFrame::~DxgiFrame() = default; + +bool DxgiFrame::Prepare(DesktopSize size, DesktopCapturer::SourceId source_id) { + if (source_id != source_id_) { + // Once the source has been changed, the entire source should be copied. + source_id_ = source_id; + context_.Reset(); + } + + if (resolution_change_detector_.IsChanged(size)) { + // Once the output size changed, recreate the SharedDesktopFrame. + frame_.reset(); + resolution_change_detector_.Reset(); + } + + if (!frame_) { + std::unique_ptr frame; + if (factory_) { + frame = SharedMemoryDesktopFrame::Create(size, factory_); + } else { + frame.reset(new BasicDesktopFrame(size)); + } + if (!frame) { + return false; + } + + frame_ = SharedDesktopFrame::Wrap(std::move(frame)); + } + + return !!frame_; +} + +SharedDesktopFrame* DxgiFrame::frame() const { + RTC_DCHECK(frame_); + return frame_.get(); +} + +DxgiFrame::Context* DxgiFrame::context() { + RTC_DCHECK(frame_); + return &context_; +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/dxgi_frame.h b/webrtc/modules/desktop_capture/win/dxgi_frame.h new file mode 100644 index 0000000000..e31c5b8b36 --- /dev/null +++ b/webrtc/modules/desktop_capture/win/dxgi_frame.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2017 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 WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_FRAME_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_FRAME_H_ + +#include +#include + +#include "webrtc/modules/desktop_capture/desktop_capturer.h" +#include "webrtc/modules/desktop_capture/desktop_capture_types.h" +#include "webrtc/modules/desktop_capture/desktop_geometry.h" +#include "webrtc/modules/desktop_capture/resolution_change_detector.h" +#include "webrtc/modules/desktop_capture/shared_desktop_frame.h" +#include "webrtc/modules/desktop_capture/shared_memory.h" +#include "webrtc/modules/desktop_capture/win/dxgi_context.h" + +namespace webrtc { + +class DxgiDuplicatorController; + +// A pair of a SharedDesktopFrame and a DxgiDuplicatorController::Context for +// the client of DxgiDuplicatorController. +class DxgiFrame final { + public: + using Context = DxgiFrameContext; + + // DxgiFrame does not take ownership of |factory|, consumers should ensure it + // outlives this instance. nullptr is acceptable. + explicit DxgiFrame(SharedMemoryFactory* factory); + ~DxgiFrame(); + + // Should not be called if Prepare() is not executed or returns false. + SharedDesktopFrame* frame() const; + + private: + // Allows DxgiDuplicatorController to access Prepare() and context() function + // as well as Context class. + friend class DxgiDuplicatorController; + + // Prepares current instance with desktop size and source id. + bool Prepare(DesktopSize size, DesktopCapturer::SourceId source_id); + + // Should not be called if Prepare() is not executed or returns false. + Context* context(); + + SharedMemoryFactory* const factory_; + ResolutionChangeDetector resolution_change_detector_; + DesktopCapturer::SourceId source_id_ = kFullDesktopScreenId; + std::unique_ptr frame_; + Context context_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_FRAME_H_ diff --git a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h index 2e85bf7a08..7d2c2edef6 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h +++ b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h @@ -26,6 +26,7 @@ #include "webrtc/modules/desktop_capture/desktop_frame_rotation.h" #include "webrtc/modules/desktop_capture/shared_desktop_frame.h" #include "webrtc/modules/desktop_capture/win/d3d_device.h" +#include "webrtc/modules/desktop_capture/win/dxgi_context.h" #include "webrtc/modules/desktop_capture/win/dxgi_texture.h" namespace webrtc { @@ -34,12 +35,7 @@ namespace webrtc { // video card. None of functions in this class is thread-safe. class DxgiOutputDuplicator { public: - struct Context { - // The updated region DxgiOutputDuplicator::DetectUpdatedRegion() output - // during last Duplicate() function call. It's always relative to the - // (0, 0). - DesktopRegion updated_region; - }; + using Context = DxgiOutputContext; // Creates an instance of DxgiOutputDuplicator from a D3dDevice and one of its // IDXGIOutput1. Caller must maintain the lifetime of device, to make sure it diff --git a/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc b/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc index 36e43fbad7..9630265c63 100644 --- a/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc +++ b/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc @@ -18,7 +18,6 @@ #include "webrtc/base/ptr_util.h" #include "webrtc/base/timeutils.h" #include "webrtc/modules/desktop_capture/desktop_frame.h" -#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h" namespace webrtc { @@ -55,97 +54,55 @@ void ScreenCapturerWinDirectx::SetSharedMemoryFactory( shared_memory_factory_ = std::move(shared_memory_factory); } -DesktopSize ScreenCapturerWinDirectx::SelectedDesktopSize() const { - if (current_screen_id_ == kFullDesktopScreenId) { - return DxgiDuplicatorController::Instance()->desktop_size(); - } - return DxgiDuplicatorController::Instance() - ->ScreenRect(current_screen_id_) - .size(); -} - void ScreenCapturerWinDirectx::CaptureFrame() { RTC_DCHECK(callback_); int64_t capture_start_time_nanos = rtc::TimeNanos(); - // The dxgi components and APIs do not update the screen resolution without - // a reinitialization. So we use the GetDC() function to retrieve the screen - // resolution to decide whether dxgi components need to be reinitialized. - // If the screen resolution changed, it's very likely the next Duplicate() - // function call will fail because of a missing monitor or the frame size is - // not enough to store the output. So we reinitialize dxgi components in-place - // to avoid a capture failure. - // But there is no guarantee GetDC() function returns the same resolution as - // dxgi APIs, we still rely on dxgi components to return the output frame - // size. - // TODO(zijiehe): Confirm whether IDXGIOutput::GetDesc() and - // IDXGIOutputDuplication::GetDesc() can detect the resolution change without - // reinitialization. - if (resolution_change_detector_.IsChanged( - GetScreenRect(kFullDesktopScreenId, std::wstring()).size())) { - frames_.Reset(); - DxgiDuplicatorController::Instance()->Reset(); - resolution_change_detector_.Reset(); - } - frames_.MoveToNextFrame(); if (!frames_.current_frame()) { - std::unique_ptr new_frame; - if (shared_memory_factory_) { - new_frame = SharedMemoryDesktopFrame::Create( - SelectedDesktopSize(), shared_memory_factory_.get()); - } else { - new_frame.reset(new BasicDesktopFrame(SelectedDesktopSize())); - } - if (!new_frame) { + frames_.ReplaceCurrentFrame( + rtc::MakeUnique(shared_memory_factory_.get())); + } + + DxgiDuplicatorController::Result result; + if (current_screen_id_ == kFullDesktopScreenId) { + result = DxgiDuplicatorController::Instance()->Duplicate( + frames_.current_frame()); + } else { + result = DxgiDuplicatorController::Instance()->DuplicateMonitor( + frames_.current_frame(), current_screen_id_); + } + + using DuplicateResult = DxgiDuplicatorController::Result; + switch (result) { + case DuplicateResult::FRAME_PREPARE_FAILED: { LOG(LS_ERROR) << "Failed to allocate a new DesktopFrame."; // This usually means we do not have enough memory or SharedMemoryFactory // cannot work correctly. callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); - return; + break; } - frames_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(new_frame))); - } - contexts_.MoveToNextFrame(); - if (!contexts_.current_frame()) { - contexts_.ReplaceCurrentFrame( - rtc::MakeUnique()); - } - - if (current_screen_id_ == kFullDesktopScreenId) { - if (!DxgiDuplicatorController::Instance()->Duplicate( - contexts_.current_frame(), frames_.current_frame())) { - // Screen size may be changed, so we need to reset the frames. - frames_.Reset(); - resolution_change_detector_.Reset(); + case DuplicateResult::INVALID_MONITOR_ID: { + callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); + break; + } + case DuplicateResult::INITIALIZATION_FAILED: + case DuplicateResult::DUPLICATION_FAILED: { callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); - return; + break; } - } else { - if (!DxgiDuplicatorController::Instance()->DuplicateMonitor( - contexts_.current_frame(), current_screen_id_, - frames_.current_frame())) { - // Screen size may be changed, so we need to reset the frames. - frames_.Reset(); - resolution_change_detector_.Reset(); - if (current_screen_id_ >= - DxgiDuplicatorController::Instance()->ScreenCount()) { - // Current monitor has been removed from the system. - callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); - } else { - callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); - } - return; + case DuplicateResult::SUCCEEDED: { + std::unique_ptr frame = + frames_.current_frame()->frame()->Share(); + frame->set_capture_time_ms( + (rtc::TimeNanos() - capture_start_time_nanos) / + rtc::kNumNanosecsPerMillisec); + frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinDirectx); + callback_->OnCaptureResult(Result::SUCCESS, std::move(frame)); + break; } } - - std::unique_ptr result = frames_.current_frame()->Share(); - result->set_capture_time_ms( - (rtc::TimeNanos() - capture_start_time_nanos) / - rtc::kNumNanosecsPerMillisec); - result->set_capturer_id(DesktopCapturerId::kScreenCapturerWinDirectx); - callback_->OnCaptureResult(Result::SUCCESS, std::move(result)); } bool ScreenCapturerWinDirectx::GetSourceList(SourceList* sources) { @@ -161,18 +118,14 @@ bool ScreenCapturerWinDirectx::SelectSource(SourceId id) { return true; } - // Changing target screen may or may not impact frame size. So resetting - // frames only when a Duplicate() function call returns false. if (id == kFullDesktopScreenId) { current_screen_id_ = id; - contexts_.Reset(); return true; } int screen_count = DxgiDuplicatorController::Instance()->ScreenCount(); if (id >= 0 && id < screen_count) { current_screen_id_ = id; - contexts_.Reset(); return true; } return false; diff --git a/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.h b/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.h index 82f8be3658..59f457c5b4 100644 --- a/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.h +++ b/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.h @@ -19,10 +19,9 @@ #include "webrtc/modules/desktop_capture/desktop_capturer.h" #include "webrtc/modules/desktop_capture/desktop_capture_options.h" #include "webrtc/modules/desktop_capture/desktop_region.h" -#include "webrtc/modules/desktop_capture/resolution_change_detector.h" #include "webrtc/modules/desktop_capture/screen_capture_frame_queue.h" -#include "webrtc/modules/desktop_capture/shared_desktop_frame.h" #include "webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h" +#include "webrtc/modules/desktop_capture/win/dxgi_frame.h" namespace webrtc { @@ -56,16 +55,10 @@ class ScreenCapturerWinDirectx : public DesktopCapturer { bool SelectSource(SourceId id) override; private: - // Returns desktop size of selected screen. - DesktopSize SelectedDesktopSize() const; - - // TODO(zijiehe): Merge |frames_| and |contexts_| into a single object. - ScreenCaptureFrameQueue frames_; - ScreenCaptureFrameQueue contexts_; + ScreenCaptureFrameQueue frames_; std::unique_ptr shared_memory_factory_; Callback* callback_ = nullptr; SourceId current_screen_id_ = kFullDesktopScreenId; - ResolutionChangeDetector resolution_change_detector_; RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinDirectx); };