diff --git a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc index 443770f1ee..52bf2a194b 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc @@ -144,4 +144,17 @@ DesktopRect DxgiAdapterDuplicator::ScreenRect(int id) const { return duplicators_[id].desktop_rect(); } +int DxgiAdapterDuplicator::screen_count() const { + return static_cast(duplicators_.size()); +} + +int64_t DxgiAdapterDuplicator::GetNumFramesCaptured() const { + int64_t min = INT64_MAX; + for (const auto& duplicator : duplicators_) { + min = std::min(min, duplicator.num_frames_captured()); + } + + return min; +} + } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h index 0261105ea0..a1ef0438c6 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h +++ b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h @@ -70,17 +70,18 @@ class DxgiAdapterDuplicator { // Returns the count of screens owned by this DxgiAdapterDuplicator. These // screens can be retrieved by an interger in the range of // [0, screen_count()). - int screen_count() const { return static_cast(duplicators_.size()); } - - private: - friend class DxgiDuplicatorController; - - bool DoInitialize(); + int screen_count() const; void Setup(Context* context); void Unregister(const Context* const context); + // The minimum num_frames_captured() returned by |duplicators_|. + int64_t GetNumFramesCaptured() const; + + private: + bool DoInitialize(); + const D3dDevice device_; std::vector duplicators_; DesktopRect desktop_rect_; diff --git a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc index 065a6148c0..a5f09f0040 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc @@ -16,8 +16,10 @@ #include #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/screen_capture_utils.h" +#include "webrtc/system_wrappers/include/sleep.h" namespace webrtc { @@ -104,14 +106,7 @@ DesktopRect DxgiDuplicatorController::ScreenRect(int id) { int DxgiDuplicatorController::ScreenCount() { rtc::CritScope lock(&lock_); - if (!Initialize()) { - return 0; - } - int result = 0; - for (auto& duplicator : duplicators_) { - result += duplicator.screen_count(); - } - return result; + return ScreenCountUnlocked(); } void DxgiDuplicatorController::Unregister(const Context* const context) { @@ -235,7 +230,11 @@ bool DxgiDuplicatorController::DoDuplicate(Context* context, if (DoDuplicateUnlocked(context, monitor_id, target)) { return true; } - Deinitialize(); + 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; } @@ -256,18 +255,41 @@ bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context, } Setup(context); + + if (!EnsureFrameCaptured(context, target)) { + return false; + } + + bool result = false; if (monitor_id < 0) { // Capture entire screen. - for (size_t i = 0; i < duplicators_.size(); i++) { - if (!duplicators_[i].Duplicate(&context->contexts_[i], target)) { - return false; - } - } + result = DoDuplicateAll(context, target); + } else { + result = DoDuplicateOne(context, monitor_id, target); + } + + if (result) { target->set_dpi(dpi()); return true; } - // Capture one monitor. + return false; +} + +bool DxgiDuplicatorController::DoDuplicateAll(Context* context, + SharedDesktopFrame* target) { + for (size_t i = 0; i < duplicators_.size(); i++) { + if (!duplicators_[i].Duplicate(&context->contexts_[i], target)) { + return false; + } + } + return true; +} + +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(); i++) { if (monitor_id >= duplicators_[i].screen_count()) { @@ -275,15 +297,83 @@ bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context, } else { if (duplicators_[i].DuplicateMonitor(&context->contexts_[i], monitor_id, target)) { - target->set_dpi(dpi()); return true; } return false; } } - // id >= ScreenCount(). This is a user error, so we do not need to - // deinitialize. return false; } +int64_t DxgiDuplicatorController::GetNumFramesCaptured() const { + int64_t min = INT64_MAX; + for (const auto& duplicator : duplicators_) { + min = std::min(min, duplicator.GetNumFramesCaptured()); + } + + return min; +} + +int DxgiDuplicatorController::ScreenCountUnlocked() { + if (!Initialize()) { + return 0; + } + int result = 0; + for (auto& duplicator : duplicators_) { + result += duplicator.screen_count(); + } + return result; +} + +bool DxgiDuplicatorController::EnsureFrameCaptured(Context* context, + SharedDesktopFrame* target) { + // On a modern system, the FPS / monitor refresh rate is usually larger than + // or equal to 60. So 17 milliseconds is enough to capture at least one frame. + const int64_t ms_per_frame = 17; + // Skips the first frame to ensure a full frame refresh has happened before + // this function returns. + const int64_t frames_to_skip = 1; + // The total time out milliseconds for this function. If we cannot get enough + // frames during this time interval, this function returns false, and cause + // the DXGI components to be reinitialized. This usually should not happen + // unless the system is switching display mode when this function is being + // called. 500 milliseconds should be enough for ~30 frames. + const int64_t timeout_ms = 500; + if (GetNumFramesCaptured() >= frames_to_skip) { + return true; + } + + std::unique_ptr fallback_frame; + SharedDesktopFrame* shared_frame = nullptr; + if (target->size().width() >= desktop_size().width() && + target->size().height() >= desktop_size().height()) { + // |target| is large enough to cover entire screen, we do not need to use + // |fallback_frame|. + shared_frame = target; + } else { + fallback_frame = SharedDesktopFrame::Wrap(std::unique_ptr( + new BasicDesktopFrame(desktop_size()))); + shared_frame = fallback_frame.get(); + } + + const int64_t start_ms = rtc::TimeMillis(); + int64_t last_frame_start_ms = 0; + while (GetNumFramesCaptured() < frames_to_skip) { + if (GetNumFramesCaptured() > 0) { + // Sleep |ms_per_frame| before capturing next frame to ensure the screen + // has been updated by the video adapter. + webrtc::SleepMs( + ms_per_frame - (rtc::TimeMillis() - last_frame_start_ms)); + } + last_frame_start_ms = rtc::TimeMillis(); + if (!DoDuplicateAll(context, shared_frame)) { + return false; + } + if (rtc::TimeMillis() - start_ms > timeout_ms) { + return false; + } + } + return true; +} + } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h index 7470db9eeb..310eff02c9 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h +++ b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h @@ -171,7 +171,7 @@ class DxgiDuplicatorController { // Updates Context if needed. void Setup(Context* context); - // Do the real duplication work. |monitor_id < 0| to capture entire screen. + // Does the real duplication work. |monitor_id < 0| to capture entire screen. bool DoDuplicate(Context* context, int monitor_id, SharedDesktopFrame* target); @@ -180,6 +180,26 @@ class DxgiDuplicatorController { int monitor_id, SharedDesktopFrame* target); + // Captures all monitors. + bool DoDuplicateAll(Context* context, SharedDesktopFrame* target); + + // Captures one monitor. + bool DoDuplicateOne(Context* context, + int monitor_id, + SharedDesktopFrame* target); + + // The minimum GetNumFramesCaptured() returned by |duplicators_|. + int64_t GetNumFramesCaptured() const; + + int ScreenCountUnlocked(); + + // 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); + // This lock must be locked whenever accessing any of the following objects. rtc::CriticalSection lock_; diff --git a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc index e2b2235352..ebd8a6aeb5 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc @@ -31,7 +31,10 @@ using Microsoft::WRL::ComPtr; namespace { // Timeout for AcquireNextFrame() call. -const int kAcquireTimeoutMs = 10; +// DxgiDuplicatorController leverages external components to do the capture +// scheduling. So here DxgiOutputDuplicator does not need to actively wait for a +// new frame. 1 millisecond is the minimium value AcquireNextFrame() accepts. +const int kAcquireTimeoutMs = 1; DesktopRect RECTToDesktopRect(const RECT& rect) { return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); @@ -204,6 +207,7 @@ bool DxgiOutputDuplicator::Duplicate(Context* context, last_frame_ = target->Share(); last_frame_offset_ = offset; target->mutable_updated_region()->AddRegion(updated_region); + num_frames_captured_++; return texture_->Release() && ReleaseFrame(); } @@ -338,4 +342,11 @@ void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) { } } +int64_t DxgiOutputDuplicator::num_frames_captured() const { +#if !defined(NDEBUG) + RTC_DCHECK_EQ(!!last_frame_, num_frames_captured_ > 0); +#endif + return num_frames_captured_; +} + } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h index 02a781487e..5a7053ab25 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h +++ b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h @@ -72,9 +72,14 @@ class DxgiOutputDuplicator { // Returns the desktop rect covered by this DxgiOutputDuplicator. DesktopRect desktop_rect() const { return desktop_rect_; } - private: - friend class DxgiAdapterDuplicator; + void Setup(Context* context); + void Unregister(const Context* const context); + + // How many frames have been captured by this DxigOutputDuplicator. + int64_t num_frames_captured() const; + + private: // Detects updated region translated by offset from IDXGIOutput1. This // function will set the |updated_region| as entire DesktopRect starts from // offset if it failed to execute Windows APIs. @@ -97,10 +102,6 @@ class DxgiOutputDuplicator { // by offset. DesktopRect TranslatedDesktopRect(DesktopVector offset); - void Setup(Context* context); - - void Unregister(const Context* const context); - // Spreads changes from |context| to other registered Context(s) in // contexts_. void SpreadContextChange(const Context* const context); @@ -126,6 +127,8 @@ class DxgiOutputDuplicator { // |last_frame_|. std::unique_ptr last_frame_; DesktopVector last_frame_offset_; + + int64_t num_frames_captured_; }; } // namespace webrtc