Skip to content

Commit

Permalink
BlankDetectorDesktopCapturerWrapper to detect a blank DesktopFrame
Browse files Browse the repository at this point in the history
DXGI capturer highly depends on video adapter and its driver, as well as Windows
itself. I recently found it cannot work on my virtualbox instance any more,
which indicates it may not work well on some specific systems. What worse is,
the APIs do not return a failure in such case.

So this change adds a BlankDetectorDesktopCapturerWrapper, which samples several
pixels in the frame returned by a DesktopCapturer implementation. If all the
pixels selected are blank, this wrapper returns a failure. A typical usage is to
combine BlankDetectorDesktopCapturerWrapper with FallbackDesktopCapturerWrapper,
and use GDI capturer in case of failure.

Usually less than 10000 pixels are checked, so the
BlankDetectorDesktopCapturerWrapper should not significant impact the capturer
performance.

This change is expected to resolve bug 682112 in another dimension.

BUG=chromium:682112

Review-Url: https://codereview.webrtc.org/2709523003
Cr-Original-Commit-Position: refs/heads/master@{#16984}
Committed: https://chromium.googlesource.com/external/webrtc/+/c4e9d210b3516c7b2faa32f24409a2e626599255
Review-Url: https://codereview.webrtc.org/2709523003
Cr-Commit-Position: refs/heads/master@{#17024}
(cherry picked from commit ccf57a7)

Review-Url: https://codereview.webrtc.org/2748813002 .
Cr-Commit-Position: refs/branch-heads/58@{#2}
Cr-Branched-From: f31969a-refs/heads/master@{#16937}
  • Loading branch information
SergeyUlanov committed Mar 13, 2017
1 parent c279861 commit c622c51
Show file tree
Hide file tree
Showing 6 changed files with 378 additions and 30 deletions.
31 changes: 8 additions & 23 deletions webrtc/modules/desktop_capture/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ if (rtc_include_tests) {
":screen_drawer",
"../../base:rtc_base",
"../../base:rtc_base_approved",
"../../system_wrappers:system_wrappers",
"../../system_wrappers",
"../../test:test_support",
"../../test:video_test_support",
]
Expand All @@ -58,6 +58,7 @@ if (rtc_include_tests) {
rtc_source_set("desktop_capture_unittests") {
testonly = true
sources = [
"blank_detector_desktop_capturer_wrapper_unittest.cc",
"desktop_and_cursor_composer_unittest.cc",
"desktop_capturer_differ_wrapper_unittest.cc",
"desktop_frame_rotation_unittest.cc",
Expand All @@ -77,10 +78,9 @@ if (rtc_include_tests) {
":desktop_capture",
":desktop_capture_mock",
":primitives",
":rgba_color",
"../..:webrtc_common",
"../../base:rtc_base_approved",
"../../system_wrappers:system_wrappers",
"../../system_wrappers",
"../../test:test_support",
"//testing/gmock",
]
Expand All @@ -99,29 +99,11 @@ if (rtc_include_tests) {
}
}

source_set("rgba_color") {
testonly = true

public_deps = [
":desktop_capture",
]

sources = [
"rgba_color.cc",
"rgba_color.h",
]

deps = [
":primitives",
"../..:webrtc_common",
]
}

source_set("screen_drawer") {
testonly = true

public_deps = [
":rgba_color",
":desktop_capture",
]

sources = [
Expand All @@ -144,7 +126,6 @@ if (rtc_include_tests) {

public_deps = [
":desktop_capture",
":rgba_color",
"//testing/gmock",
]

Expand All @@ -167,6 +148,8 @@ if (rtc_include_tests) {

rtc_static_library("desktop_capture") {
sources = [
"blank_detector_desktop_capturer_wrapper.cc",
"blank_detector_desktop_capturer_wrapper.h",
"cropped_desktop_frame.cc",
"cropped_desktop_frame.h",
"cropping_window_capturer.cc",
Expand Down Expand Up @@ -205,6 +188,8 @@ rtc_static_library("desktop_capture") {
"mouse_cursor_monitor_win.cc",
"resolution_change_detector.cc",
"resolution_change_detector.h",
"rgba_color.cc",
"rgba_color.h",
"screen_capture_frame_queue.h",
"screen_capturer_helper.cc",
"screen_capturer_helper.h",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* 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/blank_detector_desktop_capturer_wrapper.h"

#include <algorithm>
#include <utility>

#include "webrtc/base/checks.h"
#include "webrtc/modules/desktop_capture/desktop_geometry.h"

namespace webrtc {

BlankDetectorDesktopCapturerWrapper::BlankDetectorDesktopCapturerWrapper(
std::unique_ptr<DesktopCapturer> capturer,
RgbaColor blank_pixel)
: capturer_(std::move(capturer)),
blank_pixel_(blank_pixel) {
RTC_DCHECK(capturer_);
}

BlankDetectorDesktopCapturerWrapper::~BlankDetectorDesktopCapturerWrapper() =
default;

void BlankDetectorDesktopCapturerWrapper::Start(
DesktopCapturer::Callback* callback) {
capturer_->Start(this);
callback_ = callback;
}

void BlankDetectorDesktopCapturerWrapper::SetSharedMemoryFactory(
std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory));
}

void BlankDetectorDesktopCapturerWrapper::CaptureFrame() {
RTC_DCHECK(callback_);
capturer_->CaptureFrame();
}

void BlankDetectorDesktopCapturerWrapper::SetExcludedWindow(WindowId window) {
capturer_->SetExcludedWindow(window);
}

bool BlankDetectorDesktopCapturerWrapper::GetSourceList(SourceList* sources) {
return capturer_->GetSourceList(sources);
}

bool BlankDetectorDesktopCapturerWrapper::SelectSource(SourceId id) {
return capturer_->SelectSource(id);
}

bool BlankDetectorDesktopCapturerWrapper::FocusOnSelectedSource() {
return capturer_->FocusOnSelectedSource();
}

void BlankDetectorDesktopCapturerWrapper::OnCaptureResult(
Result result,
std::unique_ptr<DesktopFrame> frame) {
RTC_DCHECK(callback_);
if (result != Result::SUCCESS || non_blank_frame_received_) {
callback_->OnCaptureResult(result, std::move(frame));
return;
}

RTC_DCHECK(frame);

// If nothing has been changed in current frame, we do not need to check it
// again.
if (!frame->updated_region().is_empty() || is_first_frame_) {
last_frame_is_blank_ = IsBlankFrame(*frame);
is_first_frame_ = false;
}
if (!last_frame_is_blank_) {
non_blank_frame_received_ = true;
callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
return;
}

callback_->OnCaptureResult(Result::ERROR_TEMPORARY,
std::unique_ptr<DesktopFrame>());
}

bool BlankDetectorDesktopCapturerWrapper::IsBlankFrame(
const DesktopFrame& frame) const {
// We will check 7489 pixels for a frame with 1024 x 768 resolution.
for (int i = 0; i < frame.size().width() * frame.size().height(); i += 105) {
const int x = i % frame.size().width();
const int y = i / frame.size().width();
if (!IsBlankPixel(frame, x, y)) {
return false;
}
}

// We are verifying the pixel in the center as well.
return IsBlankPixel(frame, frame.size().width() / 2,
frame.size().height() / 2);
}

bool BlankDetectorDesktopCapturerWrapper::IsBlankPixel(
const DesktopFrame& frame,
int x,
int y) const {
uint8_t* pixel_data = frame.GetFrameDataAtPos(DesktopVector(x, y));
return RgbaColor(pixel_data) == blank_pixel_;
}

} // namespace webrtc
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* 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_BLANK_DETECTOR_DESKTOP_CAPTURER_WRAPPER_H_
#define WEBRTC_MODULES_DESKTOP_CAPTURE_BLANK_DETECTOR_DESKTOP_CAPTURER_WRAPPER_H_

#include <memory>

#include "webrtc/modules/desktop_capture/desktop_capturer.h"
#include "webrtc/modules/desktop_capture/rgba_color.h"

namespace webrtc {

// A DesktopCapturer wrapper detects the return value of its owned
// DesktopCapturer implementation. If sampled pixels returned by the
// DesktopCapturer implementation all equal to the blank pixel, this wrapper
// returns ERROR_TEMPORARY. If the DesktopCapturer implementation fails for too
// many times, this wrapper returns ERROR_PERMANENT.
class BlankDetectorDesktopCapturerWrapper final
: public DesktopCapturer,
public DesktopCapturer::Callback {
public:
// Creates BlankDetectorDesktopCapturerWrapper. BlankDesktopCapturerWrapper
// takes ownership of |capturer|. The |blank_pixel| is the unmodified color
// returned by the |capturer|.
BlankDetectorDesktopCapturerWrapper(std::unique_ptr<DesktopCapturer> capturer,
RgbaColor blank_pixel);
~BlankDetectorDesktopCapturerWrapper() override;

// DesktopCapturer interface.
void Start(DesktopCapturer::Callback* callback) override;
void SetSharedMemoryFactory(
std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
void CaptureFrame() override;
void SetExcludedWindow(WindowId window) override;
bool GetSourceList(SourceList* sources) override;
bool SelectSource(SourceId id) override;
bool FocusOnSelectedSource() override;

private:
// DesktopCapturer::Callback interface.
void OnCaptureResult(Result result,
std::unique_ptr<DesktopFrame> frame) override;

bool IsBlankFrame(const DesktopFrame& frame) const;

// Detects whether pixel at (x, y) equals to |blank_pixel_|.
bool IsBlankPixel(const DesktopFrame& frame, int x, int y) const;

const std::unique_ptr<DesktopCapturer> capturer_;
const RgbaColor blank_pixel_;

// Whether a non-blank frame has been received.
bool non_blank_frame_received_ = false;

// Whether the last frame is blank.
bool last_frame_is_blank_ = false;

// Whether current frame is the first frame.
bool is_first_frame_ = true;

DesktopCapturer::Callback* callback_ = nullptr;
};

} // namespace webrtc

#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_BLANK_DETECTOR_DESKTOP_CAPTURER_WRAPPER_H_
Loading

0 comments on commit c622c51

Please sign in to comment.