Skips the first frame in DxgiDuplicatorController
A bug has been reported to complaint the ScreenCapturerWinDirectx cannot capture the first frame, which is used in "Report an issue" page. A simple solution here is to skip the first frame. This change also removes the friend relationship between DxgiDuplicatorController / DxgiAdapterDuplicator / DxgiOutputDuplicator, which is not really necessary. BUG=682112 Review-Url: https://codereview.webrtc.org/2703123002 Cr-Commit-Position: refs/heads/master@{#16815}
This commit is contained in:
@ -144,4 +144,17 @@ DesktopRect DxgiAdapterDuplicator::ScreenRect(int id) const {
|
|||||||
return duplicators_[id].desktop_rect();
|
return duplicators_[id].desktop_rect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int DxgiAdapterDuplicator::screen_count() const {
|
||||||
|
return static_cast<int>(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
|
} // namespace webrtc
|
||||||
|
|||||||
@ -70,17 +70,18 @@ class DxgiAdapterDuplicator {
|
|||||||
// Returns the count of screens owned by this DxgiAdapterDuplicator. These
|
// Returns the count of screens owned by this DxgiAdapterDuplicator. These
|
||||||
// screens can be retrieved by an interger in the range of
|
// screens can be retrieved by an interger in the range of
|
||||||
// [0, screen_count()).
|
// [0, screen_count()).
|
||||||
int screen_count() const { return static_cast<int>(duplicators_.size()); }
|
int screen_count() const;
|
||||||
|
|
||||||
private:
|
|
||||||
friend class DxgiDuplicatorController;
|
|
||||||
|
|
||||||
bool DoInitialize();
|
|
||||||
|
|
||||||
void Setup(Context* context);
|
void Setup(Context* context);
|
||||||
|
|
||||||
void Unregister(const Context* const 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_;
|
const D3dDevice device_;
|
||||||
std::vector<DxgiOutputDuplicator> duplicators_;
|
std::vector<DxgiOutputDuplicator> duplicators_;
|
||||||
DesktopRect desktop_rect_;
|
DesktopRect desktop_rect_;
|
||||||
|
|||||||
@ -16,8 +16,10 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "webrtc/base/checks.h"
|
#include "webrtc/base/checks.h"
|
||||||
|
#include "webrtc/base/timeutils.h"
|
||||||
#include "webrtc/modules/desktop_capture/desktop_capture_types.h"
|
#include "webrtc/modules/desktop_capture/desktop_capture_types.h"
|
||||||
#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
|
#include "webrtc/modules/desktop_capture/win/screen_capture_utils.h"
|
||||||
|
#include "webrtc/system_wrappers/include/sleep.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -104,14 +106,7 @@ DesktopRect DxgiDuplicatorController::ScreenRect(int id) {
|
|||||||
|
|
||||||
int DxgiDuplicatorController::ScreenCount() {
|
int DxgiDuplicatorController::ScreenCount() {
|
||||||
rtc::CritScope lock(&lock_);
|
rtc::CritScope lock(&lock_);
|
||||||
if (!Initialize()) {
|
return ScreenCountUnlocked();
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int result = 0;
|
|
||||||
for (auto& duplicator : duplicators_) {
|
|
||||||
result += duplicator.screen_count();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DxgiDuplicatorController::Unregister(const Context* const context) {
|
void DxgiDuplicatorController::Unregister(const Context* const context) {
|
||||||
@ -235,7 +230,11 @@ bool DxgiDuplicatorController::DoDuplicate(Context* context,
|
|||||||
if (DoDuplicateUnlocked(context, monitor_id, target)) {
|
if (DoDuplicateUnlocked(context, monitor_id, target)) {
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -256,18 +255,41 @@ bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
Setup(context);
|
Setup(context);
|
||||||
|
|
||||||
|
if (!EnsureFrameCaptured(context, target)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool result = false;
|
||||||
if (monitor_id < 0) {
|
if (monitor_id < 0) {
|
||||||
// Capture entire screen.
|
// Capture entire screen.
|
||||||
for (size_t i = 0; i < duplicators_.size(); i++) {
|
result = DoDuplicateAll(context, target);
|
||||||
if (!duplicators_[i].Duplicate(&context->contexts_[i], target)) {
|
} else {
|
||||||
return false;
|
result = DoDuplicateOne(context, monitor_id, target);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (result) {
|
||||||
target->set_dpi(dpi());
|
target->set_dpi(dpi());
|
||||||
return true;
|
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();
|
for (size_t i = 0; i < duplicators_.size() && i < context->contexts_.size();
|
||||||
i++) {
|
i++) {
|
||||||
if (monitor_id >= duplicators_[i].screen_count()) {
|
if (monitor_id >= duplicators_[i].screen_count()) {
|
||||||
@ -275,15 +297,83 @@ bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context,
|
|||||||
} else {
|
} else {
|
||||||
if (duplicators_[i].DuplicateMonitor(&context->contexts_[i], monitor_id,
|
if (duplicators_[i].DuplicateMonitor(&context->contexts_[i], monitor_id,
|
||||||
target)) {
|
target)) {
|
||||||
target->set_dpi(dpi());
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// id >= ScreenCount(). This is a user error, so we do not need to
|
|
||||||
// deinitialize.
|
|
||||||
return false;
|
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<SharedDesktopFrame> 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<DesktopFrame>(
|
||||||
|
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
|
} // namespace webrtc
|
||||||
|
|||||||
@ -171,7 +171,7 @@ class DxgiDuplicatorController {
|
|||||||
// Updates Context if needed.
|
// Updates Context if needed.
|
||||||
void Setup(Context* context);
|
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,
|
bool DoDuplicate(Context* context,
|
||||||
int monitor_id,
|
int monitor_id,
|
||||||
SharedDesktopFrame* target);
|
SharedDesktopFrame* target);
|
||||||
@ -180,6 +180,26 @@ class DxgiDuplicatorController {
|
|||||||
int monitor_id,
|
int monitor_id,
|
||||||
SharedDesktopFrame* target);
|
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.
|
// This lock must be locked whenever accessing any of the following objects.
|
||||||
rtc::CriticalSection lock_;
|
rtc::CriticalSection lock_;
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,10 @@ using Microsoft::WRL::ComPtr;
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Timeout for AcquireNextFrame() call.
|
// 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) {
|
DesktopRect RECTToDesktopRect(const RECT& rect) {
|
||||||
return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
|
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_ = target->Share();
|
||||||
last_frame_offset_ = offset;
|
last_frame_offset_ = offset;
|
||||||
target->mutable_updated_region()->AddRegion(updated_region);
|
target->mutable_updated_region()->AddRegion(updated_region);
|
||||||
|
num_frames_captured_++;
|
||||||
return texture_->Release() && ReleaseFrame();
|
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
|
} // namespace webrtc
|
||||||
|
|||||||
@ -72,9 +72,14 @@ class DxgiOutputDuplicator {
|
|||||||
// Returns the desktop rect covered by this DxgiOutputDuplicator.
|
// Returns the desktop rect covered by this DxgiOutputDuplicator.
|
||||||
DesktopRect desktop_rect() const { return desktop_rect_; }
|
DesktopRect desktop_rect() const { return desktop_rect_; }
|
||||||
|
|
||||||
private:
|
void Setup(Context* context);
|
||||||
friend class DxgiAdapterDuplicator;
|
|
||||||
|
|
||||||
|
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
|
// Detects updated region translated by offset from IDXGIOutput1. This
|
||||||
// function will set the |updated_region| as entire DesktopRect starts from
|
// function will set the |updated_region| as entire DesktopRect starts from
|
||||||
// offset if it failed to execute Windows APIs.
|
// offset if it failed to execute Windows APIs.
|
||||||
@ -97,10 +102,6 @@ class DxgiOutputDuplicator {
|
|||||||
// by offset.
|
// by offset.
|
||||||
DesktopRect TranslatedDesktopRect(DesktopVector 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
|
// Spreads changes from |context| to other registered Context(s) in
|
||||||
// contexts_.
|
// contexts_.
|
||||||
void SpreadContextChange(const Context* const context);
|
void SpreadContextChange(const Context* const context);
|
||||||
@ -126,6 +127,8 @@ class DxgiOutputDuplicator {
|
|||||||
// |last_frame_|.
|
// |last_frame_|.
|
||||||
std::unique_ptr<SharedDesktopFrame> last_frame_;
|
std::unique_ptr<SharedDesktopFrame> last_frame_;
|
||||||
DesktopVector last_frame_offset_;
|
DesktopVector last_frame_offset_;
|
||||||
|
|
||||||
|
int64_t num_frames_captured_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
Reference in New Issue
Block a user