Merge ScreenCapturerWinDirectx::frames_ and contexts_
The key change of this CL is to merge ScreenCapturerWinDirectx::frames_ and
contexts_ into a new DxgiFrame class. So consumers of DxgiDuplicateController
does not need to maintain two objects. DxgiDuplicateController::Duplicate*()
functions are also updated to accept DxgiFrame parameter instead of
SharedDesktopFrame + Context. The advantages of this change are,
1. Once the screen resolution changes or an existing monitor has been removed,
DxgiFrame can automatically reset the frame without needing to return a capture
failure.
2. Remove public APIs of DxgiDuplicatorController. Some public APIs are not
needed anymore, i.e. consumers of DxgiDuplicatorController do not need to take
care about these internal states anymore. It also helps to remove several lock
acquiements.
3. Reduce the complexity of ScreenCapturerWinDirectx.
But the disadvantage is, instead of a boolean value,
DxgiDuplicateController::Duplicate*() now return an enumeration. Clients need to
use the enumeration to decide whether the error can be recovered or not.
This change also removes a duplicating logic in ScreenCapturerWinDirectx. i.e.
ResolutionChangeDetector, DxgiDuplicateController now takes care of the screen
resolution changes.
I have verified the scenarios with and without SharedMemoryFactory, also the
Desktop capture API example. So far no regression is detected.
BUG=704205
Review-Url: https://codereview.webrtc.org/2788863006
Cr-Original-Commit-Position: refs/heads/master@{#17795}
Cr-Mirrored-From: https://chromium.googlesource.com/external/webrtc
Cr-Mirrored-Commit: cf5753df778c10ec1034076d1f315fd27fad183d
diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn
index f8d5293..35ca3a4 100644
--- a/modules/desktop_capture/BUILD.gn
+++ b/modules/desktop_capture/BUILD.gn
@@ -203,8 +203,12 @@
"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/modules/desktop_capture/win/dxgi_adapter_duplicator.cc b/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
index 52bf2a1..caef1c2 100644
--- a/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
+++ b/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
@@ -31,10 +31,6 @@
} // 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/modules/desktop_capture/win/dxgi_adapter_duplicator.h b/modules/desktop_capture/win/dxgi_adapter_duplicator.h
index a1ef043..4aee8b8 100644
--- a/modules/desktop_capture/win/dxgi_adapter_duplicator.h
+++ b/modules/desktop_capture/win/dxgi_adapter_duplicator.h
@@ -16,9 +16,9 @@
#include <vector>
#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 @@
// single video card.
class DxgiAdapterDuplicator {
public:
- struct Context {
- Context();
- Context(const Context& other);
- ~Context();
-
- // Child DxgiOutputDuplicator::Context belongs to this
- // DxgiAdapterDuplicator::Context.
- std::vector<DxgiOutputDuplicator::Context> contexts;
- };
+ using Context = DxgiAdapterContext;
// Creates an instance of DxgiAdapterDuplicator from a D3dDevice. Only
// DxgiDuplicatorController can create an instance.
diff --git a/modules/desktop_capture/win/dxgi_context.cc b/modules/desktop_capture/win/dxgi_context.cc
new file mode 100644
index 0000000..dffefc4
--- /dev/null
+++ b/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/modules/desktop_capture/win/dxgi_context.h b/modules/desktop_capture/win/dxgi_context.h
new file mode 100644
index 0000000..323ea7c
--- /dev/null
+++ b/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 <vector>
+#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<DxgiOutputContext> 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<DxgiAdapterContext> contexts;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_CONTEXT_H_
diff --git a/modules/desktop_capture/win/dxgi_duplicator_controller.cc b/modules/desktop_capture/win/dxgi_duplicator_controller.cc
index a5f09f0..12876c2 100644
--- a/modules/desktop_capture/win/dxgi_duplicator_controller.cc
+++ b/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 @@
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 @@
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 @@
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 @@
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 @@
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::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 @@
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 @@
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 @@
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/modules/desktop_capture/win/dxgi_duplicator_controller.h b/modules/desktop_capture/win/dxgi_duplicator_controller.h
index 310eff0..364690b 100644
--- a/modules/desktop_capture/win/dxgi_duplicator_controller.h
+++ b/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 @@
// 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<DxgiAdapterDuplicator::Context> contexts_;
- };
+ using Context = DxgiFrameContext;
// A collection of D3d information we are interested on, which may impact
// capturer performance or reliability.
@@ -78,87 +57,80 @@
// 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 @@
// 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 @@
// 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 @@
// 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/modules/desktop_capture/win/dxgi_frame.cc b/modules/desktop_capture/win/dxgi_frame.cc
new file mode 100644
index 0000000..0ba678d
--- /dev/null
+++ b/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 <utility>
+
+#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<DesktopFrame> 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/modules/desktop_capture/win/dxgi_frame.h b/modules/desktop_capture/win/dxgi_frame.h
new file mode 100644
index 0000000..e31c5b8
--- /dev/null
+++ b/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 <memory>
+#include <vector>
+
+#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<SharedDesktopFrame> frame_;
+ Context context_;
+};
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_FRAME_H_
diff --git a/modules/desktop_capture/win/dxgi_output_duplicator.h b/modules/desktop_capture/win/dxgi_output_duplicator.h
index 2e85bf7..7d2c2ed 100644
--- a/modules/desktop_capture/win/dxgi_output_duplicator.h
+++ b/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 @@
// 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/modules/desktop_capture/win/screen_capturer_win_directx.cc b/modules/desktop_capture/win/screen_capturer_win_directx.cc
index 36e43fb..9630265 100644
--- a/modules/desktop_capture/win/screen_capturer_win_directx.cc
+++ b/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 @@
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<DesktopFrame> 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<DxgiFrame>(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<DxgiDuplicatorController::Context>());
- }
-
- 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<DesktopFrame> 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<DesktopFrame> 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 @@
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/modules/desktop_capture/win/screen_capturer_win_directx.h b/modules/desktop_capture/win/screen_capturer_win_directx.h
index 82f8be3..59f457c 100644
--- a/modules/desktop_capture/win/screen_capturer_win_directx.h
+++ b/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 @@
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<SharedDesktopFrame> frames_;
- ScreenCaptureFrameQueue<DxgiDuplicatorController::Context> contexts_;
+ ScreenCaptureFrameQueue<DxgiFrame> frames_;
std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
Callback* callback_ = nullptr;
SourceId current_screen_id_ = kFullDesktopScreenId;
- ResolutionChangeDetector resolution_change_detector_;
RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinDirectx);
};