diff --git a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc index abe7908afe..1b822161d0 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc @@ -113,11 +113,10 @@ void DxgiAdapterDuplicator::Unregister(const Context* const context) { } bool DxgiAdapterDuplicator::Duplicate(Context* context, - const DesktopFrame* last_frame, - DesktopFrame* target) { + SharedDesktopFrame* target) { RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size()); for (size_t i = 0; i < duplicators_.size(); i++) { - if (!duplicators_[i].Duplicate(&context->contexts[i], last_frame, + if (!duplicators_[i].Duplicate(&context->contexts[i], duplicators_[i].desktop_rect().top_left(), target)) { return false; @@ -128,13 +127,12 @@ bool DxgiAdapterDuplicator::Duplicate(Context* context, bool DxgiAdapterDuplicator::DuplicateMonitor(Context* context, int monitor_id, - const DesktopFrame* last_frame, - DesktopFrame* target) { + SharedDesktopFrame* target) { RTC_DCHECK(monitor_id >= 0 && monitor_id < static_cast(duplicators_.size()) && context->contexts.size() == duplicators_.size()); - return duplicators_[monitor_id].Duplicate( - &context->contexts[monitor_id], last_frame, DesktopVector(), target); + return duplicators_[monitor_id].Duplicate(&context->contexts[monitor_id], + DesktopVector(), target); } DesktopRect DxgiAdapterDuplicator::ScreenRect(int id) const { diff --git a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h index be9fd17009..a9e61035fa 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h +++ b/webrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h @@ -15,9 +15,9 @@ #include -#include "webrtc/modules/desktop_capture/desktop_frame.h" #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_output_duplicator.h" @@ -44,18 +44,15 @@ class DxgiAdapterDuplicator { // Initializes the DxgiAdapterDuplicator from a D3dDevice. bool Initialize(); - // Sequential calls Duplicate function of all the DxgiOutputDuplicators owned - // by this instance. - bool Duplicate(Context* context, - const DesktopFrame* last_frame, - DesktopFrame* target); + // Sequentially calls Duplicate function of all the DxgiOutputDuplicator + // instances owned by this instance, and writes into |target|. + bool Duplicate(Context* context, SharedDesktopFrame* target); - // Captures one monitor and writes into target. |monitor_id| should be between - // [0, screen_count()). + // Captures one monitor and writes into |target|. |monitor_id| should be + // between [0, screen_count()). bool DuplicateMonitor(Context* context, int monitor_id, - const DesktopFrame* last_frame, - DesktopFrame* target); + SharedDesktopFrame* target); // Returns desktop rect covered by this DxgiAdapterDuplicator. DesktopRect desktop_rect() const { return 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 46a345846e..0276f6b941 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc @@ -176,39 +176,33 @@ void DxgiDuplicatorController::Setup(Context* context) { } bool DxgiDuplicatorController::Duplicate(Context* context, - const DesktopFrame* last_frame, - DesktopFrame* target) { - return DoDuplicate(context, -1, last_frame, target); + SharedDesktopFrame* target) { + return DoDuplicate(context, -1, target); } bool DxgiDuplicatorController::DuplicateMonitor(Context* context, int monitor_id, - const DesktopFrame* last_frame, - DesktopFrame* target) { + SharedDesktopFrame* target) { RTC_DCHECK_GE(monitor_id, 0); - return DoDuplicate(context, monitor_id, last_frame, target); + return DoDuplicate(context, monitor_id, target); } bool DxgiDuplicatorController::DoDuplicate(Context* context, int monitor_id, - const DesktopFrame* last_frame, - DesktopFrame* target) { + SharedDesktopFrame* target) { RTC_DCHECK(target); - if (last_frame && !target->size().equals(last_frame->size())) { - return false; - } target->mutable_updated_region()->Clear(); rtc::CritScope lock(&lock_); if (!Initialize()) { // Cannot initialize COM components now, display mode may be changing. return false; } + Setup(context); if (monitor_id < 0) { // Capture entire screen. for (size_t i = 0; i < duplicators_.size(); i++) { - if (!duplicators_[i].Duplicate(&context->contexts_[i], last_frame, - target)) { + if (!duplicators_[i].Duplicate(&context->contexts_[i], target)) { Deinitialize(); return false; } @@ -224,7 +218,7 @@ bool DxgiDuplicatorController::DoDuplicate(Context* context, monitor_id -= duplicators_[i].screen_count(); } else { if (duplicators_[i].DuplicateMonitor(&context->contexts_[i], monitor_id, - last_frame, target)) { + target)) { target->set_dpi(dpi()); return true; } diff --git a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h index e65035d8eb..fb24b3a676 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h +++ b/webrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h @@ -11,12 +11,13 @@ #ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_ #define WEBRTC_MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_ +#include #include #include "webrtc/base/criticalsection.h" -#include "webrtc/modules/desktop_capture/desktop_frame.h" #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_adapter_duplicator.h" @@ -33,16 +34,6 @@ namespace webrtc { // but a later Duplicate() returns false, this usually means the display mode is // changing. Consumers should retry after a while. (Typically 50 milliseconds, // but according to hardware performance, this time may vary.) -// -// This class is normally used with double buffering, e.g. as in -// ScreenCapturerWinDirectx, but it should work with consumers with one buffer, -// i.e. consumers can always send nullptr for |last_frame|. Some minor changes -// in DxgiOutputDuplicator class are nice to have to reduce size of data to copy -// (Commented in dxgi_output_duplicator.cc). But this class won't work -// with three or more buffers, the updated region merging logic will be broken -// in such scenarios. If a consumer does have this requirement, one can always -// send a new Context instance to Duplicate() function to force duplicator to -// treat it as a new consumer. class DxgiDuplicatorController { public: // A context to store the status of a single consumer of @@ -82,18 +73,15 @@ class DxgiDuplicatorController { // 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. But we should find a way to do it. - bool Duplicate(Context* context, - const DesktopFrame* last_frame, - DesktopFrame* target); + // synchronize them manually. We should find a way to do it. + bool Duplicate(Context* context, SharedDesktopFrame* target); // 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, - const DesktopFrame* last_frame, - DesktopFrame* target); + SharedDesktopFrame* target); // Returns dpi of current system. Returns an empty DesktopVector if system // does not support DXGI based capturer. @@ -153,8 +141,7 @@ class DxgiDuplicatorController { // Do the real duplication work. |monitor_id < 0| to capture entire screen. bool DoDuplicate(Context* context, int monitor_id, - const DesktopFrame* last_frame, - DesktopFrame* target); + 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 7ce17c66a4..d7eb335fc3 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc +++ b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc @@ -118,12 +118,17 @@ bool DxgiOutputDuplicator::ReleaseFrame() { } bool DxgiOutputDuplicator::Duplicate(Context* context, - const DesktopFrame* last_frame, - const DesktopVector offset, - DesktopFrame* target) { + DesktopVector offset, + SharedDesktopFrame* target) { RTC_DCHECK(duplication_); RTC_DCHECK(texture_); RTC_DCHECK(target); + if (!DesktopRect::MakeSize(target->size()) + .ContainsRect(TranslatedDesktopRect(offset))) { + // target size is not large enough to cover current output region. + return false; + } + DXGI_OUTDUPL_FRAME_INFO frame_info; memset(&frame_info, 0, sizeof(frame_info)); ComPtr resource; @@ -140,40 +145,36 @@ bool DxgiOutputDuplicator::Duplicate(Context* context, // buffering implementation, as what we have in ScreenCapturerWinDirectx. If // a consumer uses single buffering, we should clear context->updated_region // after it has been merged to updated_region. - DesktopRegion updated_region = context->updated_region; + DesktopRegion updated_region; + updated_region.Swap(&context->updated_region); if (error.Error() == S_OK && frame_info.AccumulatedFrames > 0) { DetectUpdatedRegion(frame_info, offset, &context->updated_region); - SpreadContextChange(context); - updated_region.AddRegion(context->updated_region); - if (!texture_->CopyFrom(frame_info, resource.Get(), updated_region)) { + if (!texture_->CopyFrom(frame_info, resource.Get(), + context->updated_region)) { return false; } + SpreadContextChange(context); + updated_region.AddRegion(context->updated_region); const DesktopFrame& source = texture_->AsDesktopFrame(); - DesktopRect target_rect(DesktopRect::MakeSize(target->size())); for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); it.Advance()) { - if (!target_rect.ContainsRect(it.rect())) { - // target size is not large enough to copy the pixel from texture. - return false; - } - target->CopyPixelsFrom(source, it.rect().top_left().subtract(offset), - it.rect()); + target->CopyPixelsFrom(source, SourceRect(it.rect()).top_left(), + TargetRect(it.rect(), offset)); } + last_frame_ = target->Share(); + last_frame_offset_ = offset; target->mutable_updated_region()->AddRegion(updated_region); return texture_->Release() && ReleaseFrame(); } - if (last_frame != nullptr) { - // DxgiOutputDuplicatorContainer::Duplicate() makes sure target size and - // last frame size are consistent. - RTC_DCHECK(target->size().equals(last_frame->size())); + if (last_frame_) { // No change since last frame or AcquireNextFrame() timed out, we will // export last frame to the target. - context->updated_region.Clear(); for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); it.Advance()) { - target->CopyPixelsFrom(*last_frame, it.rect().top_left(), it.rect()); + target->CopyPixelsFrom(*last_frame_, SourceRect(it.rect()).top_left(), + TargetRect(it.rect(), offset)); } target->mutable_updated_region()->AddRegion(updated_region); } @@ -182,8 +183,7 @@ bool DxgiOutputDuplicator::Duplicate(Context* context, return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame(); } -DesktopRect DxgiOutputDuplicator::TranslatedDesktopRect( - const DesktopVector offset) { +DesktopRect DxgiOutputDuplicator::TranslatedDesktopRect(DesktopVector offset) { DesktopRect result(DesktopRect::MakeSize(desktop_rect_.size())); result.Translate(offset); return result; @@ -191,7 +191,7 @@ DesktopRect DxgiOutputDuplicator::TranslatedDesktopRect( void DxgiOutputDuplicator::DetectUpdatedRegion( const DXGI_OUTDUPL_FRAME_INFO& frame_info, - const DesktopVector offset, + DesktopVector offset, DesktopRegion* updated_region) { if (DoDetectUpdatedRegion(frame_info, updated_region)) { updated_region->Translate(offset.x(), offset.y()); @@ -301,4 +301,17 @@ void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) { } } +DesktopRect DxgiOutputDuplicator::SourceRect(DesktopRect rect) { + // |texture_|->AsDesktopFrame() starts from (0, 0). + rect.Translate(-desktop_rect_.left(), -desktop_rect_.top()); + return rect; +} + +DesktopRect DxgiOutputDuplicator::TargetRect(DesktopRect rect, + DesktopVector offset) { + rect = SourceRect(rect); + rect.Translate(offset); + return rect; +} + } // 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 31fb32ab9e..020ca640ed 100644 --- a/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h +++ b/webrtc/modules/desktop_capture/win/dxgi_output_duplicator.h @@ -21,9 +21,9 @@ #include "webrtc/base/criticalsection.h" #include "webrtc/base/thread_annotations.h" -#include "webrtc/modules/desktop_capture/desktop_frame.h" #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_texture.h" @@ -60,17 +60,14 @@ class DxgiOutputDuplicator { // Copies the content of current IDXGIOutput to the |target|. To improve the // performance, this function copies only regions merged from - // |last_frame|.updated_region and DetectUpdatedRegion(). The |offset| decides - // the - // offset in the |target| where the content should be copied to. i.e. this + // |context|->updated_region and DetectUpdatedRegion(). The |offset| decides + // the offset in the |target| where the content should be copied to. i.e. this // function copies the content to the rectangle of (offset.x(), offset.y()) to // (offset.x() + desktop_rect_.width(), offset.y() + desktop_rect_.height()). - // The |last_frame| is always expected to be translated by the same offset. // Returns false in case of a failure. bool Duplicate(Context* context, - const DesktopFrame* last_frame, - const DesktopVector offset, - DesktopFrame* target); + DesktopVector offset, + SharedDesktopFrame* target); // Returns the desktop rect covered by this DxgiOutputDuplicator. DesktopRect desktop_rect() const { return desktop_rect_; } @@ -82,7 +79,7 @@ class DxgiOutputDuplicator { // function will set the |updated_region| as entire DesktopRect starts from // offset if it failed to execute Windows APIs. void DetectUpdatedRegion(const DXGI_OUTDUPL_FRAME_INFO& frame_info, - const DesktopVector offset, + DesktopVector offset, DesktopRegion* updated_region); // Returns untranslated updated region, which are directly returned by Windows @@ -98,7 +95,7 @@ class DxgiOutputDuplicator { // Returns a DesktopRect with the same size of desktop_size_, but translated // by offset. - DesktopRect TranslatedDesktopRect(const DesktopVector offset); + DesktopRect TranslatedDesktopRect(DesktopVector offset); void Setup(Context* context); @@ -108,6 +105,12 @@ class DxgiOutputDuplicator { // contexts_. void SpreadContextChange(const Context* const context); + // Returns a DesktopRect in the coordinate of |texture_|->AsDesktopFrame(). + DesktopRect SourceRect(DesktopRect rect); + + // Returns a DesktopRect in the coordinate of |offset|. + DesktopRect TargetRect(DesktopRect rect, DesktopVector offset); + const D3dDevice& device_; const Microsoft::WRL::ComPtr output_; const DesktopRect desktop_rect_; @@ -121,6 +124,12 @@ class DxgiOutputDuplicator { // change this time. And during next Duplicate() function call, their // updated_region_ will be merged and copied. std::vector contexts_; + + // The last full frame of this output and its offset. If on AcquireNextFrame() + // failed because of timeout, i.e. no update, we can copy content from + // |last_frame_|. + std::unique_ptr last_frame_; + DesktopVector last_frame_offset_; }; } // namespace webrtc 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 a09935fb01..e4b2c3e8a2 100644 --- a/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc +++ b/webrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc @@ -79,7 +79,7 @@ void ScreenCapturerWinDirectx::Capture(const DesktopRegion& region) { if (current_screen_id == kFullDesktopScreenId) { if (!DxgiDuplicatorController::Instance()->Duplicate( - &context_, frames_.previous_frame(), frames_.current_frame())) { + &context_, frames_.current_frame())) { // Screen size may be changed, so we need to reset the frames. frames_.Reset(); callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); @@ -87,8 +87,7 @@ void ScreenCapturerWinDirectx::Capture(const DesktopRegion& region) { } } else { if (!DxgiDuplicatorController::Instance()->DuplicateMonitor( - &context_, current_screen_id, frames_.previous_frame(), - frames_.current_frame())) { + &context_, current_screen_id, frames_.current_frame())) { // Screen size may be changed, so we need to reset the frames. frames_.Reset(); if (current_screen_id >=