DirectX capturer may crash after switching shared screen
The root cause is because the offset used in DxgiOutputDuplicator should always depend on the position of the monitor in the system instead of the offset in the target frame. Otherwise, once switching between two monitors with different screen size, the updated region in the context would base on the old monitor, and cause the copied regions to be out of the source DesktopFrame. This issue also impacts the SpreadContextChange() function, the updated region stores in the Context should also only depend on the position of the monitor. BUG=chromium:706797 Review-Url: https://codereview.webrtc.org/2801433002 Cr-Commit-Position: refs/heads/master@{#17548}
This commit is contained in:
parent
cf02cf13a7
commit
7343c8e09f
@ -57,12 +57,6 @@ Rotation DxgiRotationToRotation(DXGI_MODE_ROTATION rotation) {
|
||||
return Rotation::CLOCK_WISE_0;
|
||||
}
|
||||
|
||||
// Translates |rect| with the reverse of |offset|
|
||||
DesktopRect ReverseTranslate(DesktopRect rect, DesktopVector offset) {
|
||||
rect.Translate(-offset.x(), -offset.y());
|
||||
return rect;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device,
|
||||
@ -132,8 +126,7 @@ bool DxgiOutputDuplicator::DuplicateOutput() {
|
||||
}
|
||||
|
||||
rotation_ = DxgiRotationToRotation(desc_.Rotation);
|
||||
unrotated_size_ =
|
||||
RotateSize(desktop_rect_.size(), ReverseRotation(rotation_));
|
||||
unrotated_size_ = RotateSize(desktop_size(), ReverseRotation(rotation_));
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -157,7 +150,7 @@ bool DxgiOutputDuplicator::Duplicate(Context* context,
|
||||
RTC_DCHECK(texture_);
|
||||
RTC_DCHECK(target);
|
||||
if (!DesktopRect::MakeSize(target->size())
|
||||
.ContainsRect(TranslatedDesktopRect(offset))) {
|
||||
.ContainsRect(GetTranslatedDesktopRect(offset))) {
|
||||
// target size is not large enough to cover current output region.
|
||||
return false;
|
||||
}
|
||||
@ -175,37 +168,43 @@ bool DxgiOutputDuplicator::Duplicate(Context* context,
|
||||
|
||||
// We need to merge updated region with the one from context, but only spread
|
||||
// updated region from current frame. So keeps a copy of updated region from
|
||||
// context here.
|
||||
// context here. The |updated_region| always starts from (0, 0).
|
||||
DesktopRegion updated_region;
|
||||
updated_region.Swap(&context->updated_region);
|
||||
if (error.Error() == S_OK &&
|
||||
frame_info.AccumulatedFrames > 0 &&
|
||||
resource) {
|
||||
DetectUpdatedRegion(frame_info, offset, &context->updated_region);
|
||||
DetectUpdatedRegion(frame_info, &context->updated_region);
|
||||
SpreadContextChange(context);
|
||||
if (!texture_->CopyFrom(frame_info, resource.Get())) {
|
||||
return false;
|
||||
}
|
||||
SpreadContextChange(context);
|
||||
updated_region.AddRegion(context->updated_region);
|
||||
// TODO(zijiehe): Figure out why clearing context->updated_region() here
|
||||
// triggers screen flickering?
|
||||
|
||||
const DesktopFrame& source = texture_->AsDesktopFrame();
|
||||
if (rotation_ != Rotation::CLOCK_WISE_0) {
|
||||
for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
|
||||
it.Advance()) {
|
||||
const DesktopRect source_rect =
|
||||
RotateRect(ReverseTranslate(it.rect(), offset),
|
||||
desktop_rect().size(), ReverseRotation(rotation_));
|
||||
// The |updated_region| returned by Windows is rotated, but the |source|
|
||||
// frame is not. So we need to rotate it reversely.
|
||||
const DesktopRect source_rect = RotateRect(
|
||||
it.rect(), desktop_size(), ReverseRotation(rotation_));
|
||||
RotateDesktopFrame(source, source_rect, rotation_, offset, target);
|
||||
}
|
||||
} else {
|
||||
for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
|
||||
it.Advance()) {
|
||||
target->CopyPixelsFrom(
|
||||
source, ReverseTranslate(it.rect(), offset).top_left(), it.rect());
|
||||
// The DesktopRect in |target|, starts from offset.
|
||||
DesktopRect dest_rect = it.rect();
|
||||
dest_rect.Translate(offset);
|
||||
target->CopyPixelsFrom(source, it.rect().top_left(), dest_rect);
|
||||
}
|
||||
}
|
||||
last_frame_ = target->Share();
|
||||
last_frame_offset_ = offset;
|
||||
updated_region.Translate(offset.x(), offset.y());
|
||||
target->mutable_updated_region()->AddRegion(updated_region);
|
||||
num_frames_captured_++;
|
||||
return texture_->Release() && ReleaseFrame();
|
||||
@ -216,8 +215,15 @@ bool DxgiOutputDuplicator::Duplicate(Context* context,
|
||||
// export last frame to the target.
|
||||
for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
|
||||
it.Advance()) {
|
||||
target->CopyPixelsFrom(*last_frame_, it.rect().top_left(), it.rect());
|
||||
// The DesktopRect in |source|, starts from last_frame_offset_.
|
||||
DesktopRect source_rect = it.rect();
|
||||
// The DesktopRect in |target|, starts from offset.
|
||||
DesktopRect target_rect = source_rect;
|
||||
source_rect.Translate(last_frame_offset_);
|
||||
target_rect.Translate(offset);
|
||||
target->CopyPixelsFrom(*last_frame_, source_rect.top_left(), target_rect);
|
||||
}
|
||||
updated_region.Translate(offset.x(), offset.y());
|
||||
target->mutable_updated_region()->AddRegion(updated_region);
|
||||
} else {
|
||||
// If we were at the very first frame, and capturing failed, the
|
||||
@ -229,23 +235,26 @@ bool DxgiOutputDuplicator::Duplicate(Context* context,
|
||||
return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame();
|
||||
}
|
||||
|
||||
DesktopRect DxgiOutputDuplicator::TranslatedDesktopRect(DesktopVector offset) {
|
||||
DesktopRect result(DesktopRect::MakeSize(desktop_rect_.size()));
|
||||
DesktopRect DxgiOutputDuplicator::GetTranslatedDesktopRect(
|
||||
DesktopVector offset) const {
|
||||
DesktopRect result(DesktopRect::MakeSize(desktop_size()));
|
||||
result.Translate(offset);
|
||||
return result;
|
||||
}
|
||||
|
||||
DesktopRect DxgiOutputDuplicator::GetUntranslatedDesktopRect() const {
|
||||
return DesktopRect::MakeSize(desktop_size());
|
||||
}
|
||||
|
||||
void DxgiOutputDuplicator::DetectUpdatedRegion(
|
||||
const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
||||
DesktopVector offset,
|
||||
DesktopRegion* updated_region) {
|
||||
if (DoDetectUpdatedRegion(frame_info, updated_region)) {
|
||||
updated_region->Translate(offset.x(), offset.y());
|
||||
// Make sure even a region returned by Windows API is out of the scope of
|
||||
// desktop_rect_, we still won't export it to the target DesktopFrame.
|
||||
updated_region->IntersectWith(TranslatedDesktopRect(offset));
|
||||
updated_region->IntersectWith(GetUntranslatedDesktopRect());
|
||||
} else {
|
||||
updated_region->SetRect(TranslatedDesktopRect(offset));
|
||||
updated_region->SetRect(GetUntranslatedDesktopRect());
|
||||
}
|
||||
}
|
||||
|
||||
@ -340,7 +349,7 @@ bool DxgiOutputDuplicator::DoDetectUpdatedRegion(
|
||||
void DxgiOutputDuplicator::Setup(Context* context) {
|
||||
RTC_DCHECK(context->updated_region.is_empty());
|
||||
// Always copy entire monitor during the first Duplicate() function call.
|
||||
context->updated_region.AddRect(desktop_rect_);
|
||||
context->updated_region.AddRect(GetUntranslatedDesktopRect());
|
||||
RTC_DCHECK(std::find(contexts_.begin(), contexts_.end(), context) ==
|
||||
contexts_.end());
|
||||
contexts_.push_back(context);
|
||||
@ -361,6 +370,10 @@ void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) {
|
||||
}
|
||||
}
|
||||
|
||||
DesktopSize DxgiOutputDuplicator::desktop_size() const {
|
||||
return desktop_rect_.size();
|
||||
}
|
||||
|
||||
int64_t DxgiOutputDuplicator::num_frames_captured() const {
|
||||
#if !defined(NDEBUG)
|
||||
RTC_DCHECK_EQ(!!last_frame_, num_frames_captured_ > 0);
|
||||
|
||||
@ -36,8 +36,8 @@ class DxgiOutputDuplicator {
|
||||
public:
|
||||
struct Context {
|
||||
// The updated region DxgiOutputDuplicator::DetectUpdatedRegion() output
|
||||
// during last Duplicate() function call. It's a DesktopRegion translated by
|
||||
// offset of each DxgiOutputDuplicator instance.
|
||||
// during last Duplicate() function call. It's always relative to the
|
||||
// (0, 0).
|
||||
DesktopRegion updated_region;
|
||||
};
|
||||
|
||||
@ -80,11 +80,9 @@ class DxgiOutputDuplicator {
|
||||
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.
|
||||
// Calls DoDetectUpdatedRegion(). If it fails, this function sets the
|
||||
// |updated_region| as entire UntranslatedDesktopRect().
|
||||
void DetectUpdatedRegion(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
|
||||
DesktopVector offset,
|
||||
DesktopRegion* updated_region);
|
||||
|
||||
// Returns untranslated updated region, which are directly returned by Windows
|
||||
@ -98,14 +96,21 @@ class DxgiOutputDuplicator {
|
||||
// Returns false if system does not support IDXGIOutputDuplication.
|
||||
bool DuplicateOutput();
|
||||
|
||||
// Returns a DesktopRect with the same size of desktop_size_, but translated
|
||||
// Returns a DesktopRect with the same size of desktop_size(), but translated
|
||||
// by offset.
|
||||
DesktopRect TranslatedDesktopRect(DesktopVector offset);
|
||||
DesktopRect GetTranslatedDesktopRect(DesktopVector offset) const;
|
||||
|
||||
// Returns a DesktopRect with the same size of desktop_size(), but starts from
|
||||
// (0, 0).
|
||||
DesktopRect GetUntranslatedDesktopRect() const;
|
||||
|
||||
// Spreads changes from |context| to other registered Context(s) in
|
||||
// contexts_.
|
||||
void SpreadContextChange(const Context* const context);
|
||||
|
||||
// Returns the size of desktop rectangle current instance representing.
|
||||
DesktopSize desktop_size() const;
|
||||
|
||||
const D3dDevice device_;
|
||||
const Microsoft::WRL::ComPtr<IDXGIOutput1> output_;
|
||||
const DesktopRect desktop_rect_;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user