Index: webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc |
diff --git a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc |
index a5f09f00407b301b112ecd7d6d2d0b62d4508564..12876c2c89d4612731de10c6e1ba51932f1d8003 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() { |
+int DxgiDuplicatorController::ScreenCount() { |
rtc::CritScope lock(&lock_); |
if (Initialize()) { |
- return desktop_rect_; |
+ return ScreenCountUnlocked(); |
} |
- return DesktopRect(); |
+ return 0; |
} |
-DesktopSize DxgiDuplicatorController::desktop_size() { |
- DesktopRect rect = desktop_rect(); |
- return DesktopSize(rect.right(), rect.bottom()); |
-} |
- |
-DesktopRect DxgiDuplicatorController::ScreenRect(int id) { |
- RTC_DCHECK(id >= 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()) { |
- return DesktopRect(); |
+ // Cannot initialize COM components now, display mode may be changing. |
+ return Result::INITIALIZATION_FAILED; |
} |
- 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); |
- } |
+ |
+ if (!frame->Prepare(SelectedDesktopSize(monitor_id), monitor_id)) { |
+ return Result::FRAME_PREPARE_FAILED; |
} |
- return DesktopRect(); |
-} |
-int DxgiDuplicatorController::ScreenCount() { |
- rtc::CritScope lock(&lock_); |
- return ScreenCountUnlocked(); |
+ 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 |