Add CreateWindowCapturer() and CreateScreenCapturer() in DesktopCapturer

This change copies ScreenCapturerDifferWrapper to a new
DesktopCapturerDifferWrapper, and adds DesktopCapturer::CreateWindowCapturer and
DesktopCapturer::CreateScreenCapturer functions to replace
WindowCapturer::Create and ScreenCapturer::Create.

BUG=webrtc:6513

Review-Url: https://codereview.webrtc.org/2468753002
Cr-Commit-Position: refs/heads/master@{#14880}
This commit is contained in:
zijiehe 2016-11-01 16:02:44 -07:00 committed by Commit bot
parent ee8ad2bb0f
commit b763e39beb
22 changed files with 813 additions and 50 deletions

View File

@ -8,10 +8,11 @@
import("../build/webrtc.gni")
import("audio_coding/audio_coding.gni")
import("desktop_capture/desktop_capture.gni")
declare_args() {
# Desktop capturer is supported only on Windows, OSX and Linux.
rtc_desktop_capture_supported = is_win || is_mac || is_linux
rtc_desktop_capture_supported = rtc_desktop_capture_supported
}
group("modules") {
@ -102,8 +103,11 @@ if (rtc_include_tests) {
]
if (rtc_desktop_capture_supported) {
deps += [ "desktop_capture:desktop_capture_test_tools" ]
sources += [ "desktop_capture/screen_capturer_integration_test.cc" ]
deps += [ "desktop_capture:screen_drawer" ]
sources += [
"desktop_capture/screen_capturer_integration_test.cc",
"desktop_capture/screen_drawer_unittest.cc",
]
}
data = modules_tests_resources
@ -545,14 +549,10 @@ if (rtc_include_tests) {
}
if (rtc_desktop_capture_supported) {
deps += [ "desktop_capture:desktop_capture_test_tools" ]
deps += [ "desktop_capture:desktop_capture_mock" ]
sources += [
"desktop_capture/desktop_and_cursor_composer_unittest.cc",
"desktop_capture/desktop_frame_generator.cc",
"desktop_capture/desktop_frame_generator.h",
"desktop_capture/fake_desktop_capturer.h",
"desktop_capture/fake_screen_capturer.cc",
"desktop_capture/fake_screen_capturer.h",
"desktop_capture/desktop_capturer_differ_wrapper_unittest.cc",
"desktop_capture/mouse_cursor_monitor_unittest.cc",
"desktop_capture/rgba_color_unittest.cc",
"desktop_capture/screen_capturer_differ_wrapper_unittest.cc",
@ -560,7 +560,6 @@ if (rtc_include_tests) {
"desktop_capture/screen_capturer_mac_unittest.cc",
"desktop_capture/screen_capturer_mock_objects.h",
"desktop_capture/screen_capturer_unittest.cc",
"desktop_capture/screen_drawer_unittest.cc",
"desktop_capture/win/cursor_unittest.cc",
"desktop_capture/win/cursor_unittest_resources.h",
"desktop_capture/win/cursor_unittest_resources.rc",

View File

@ -8,6 +8,7 @@
import("//build/config/ui.gni")
import("../../build/webrtc.gni")
import("desktop_capture.gni")
use_desktop_capture_differ_sse2 =
!is_ios && (current_cpu == "x86" || current_cpu == "x64")
@ -31,7 +32,7 @@ rtc_static_library("primitives") {
}
if (rtc_include_tests) {
source_set("desktop_capture_test_tools") {
source_set("rgba_color") {
testonly = true
public_deps = [
@ -41,6 +42,17 @@ if (rtc_include_tests) {
sources = [
"rgba_color.cc",
"rgba_color.h",
]
}
source_set("screen_drawer") {
testonly = true
public_deps = [
":rgba_color",
]
sources = [
"screen_drawer.cc",
"screen_drawer.h",
"screen_drawer_linux.cc",
@ -48,6 +60,25 @@ if (rtc_include_tests) {
"screen_drawer_win.cc",
]
}
source_set("desktop_capture_mock") {
testonly = true
public_deps = [
":desktop_capture",
":rgba_color",
]
sources = [
"desktop_frame_generator.cc",
"desktop_frame_generator.h",
"fake_desktop_capturer.h",
"fake_screen_capturer.cc",
"fake_screen_capturer.h",
"mock_desktop_capturer_callback.h",
"screen_capturer_mock_objects.h",
]
}
}
rtc_static_library("desktop_capture") {
@ -151,6 +182,8 @@ rtc_static_library("desktop_capture") {
if (!is_ios) {
sources += [
"desktop_capturer_differ_wrapper.cc",
"desktop_capturer_differ_wrapper.h",
"differ_block.cc",
"differ_block.h",
"screen_capturer_differ_wrapper.cc",
@ -182,6 +215,10 @@ rtc_static_library("desktop_capture") {
if (use_desktop_capture_differ_sse2) {
deps += [ ":desktop_capture_differ_sse2" ]
}
if (rtc_desktop_capture_supported) {
defines = [ "RTC_DESKTOP_CAPTURE_SUPPORTED" ]
}
}
if (use_desktop_capture_differ_sse2) {

View File

@ -0,0 +1,11 @@
# Copyright (c) 2016 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.
import("../../build/webrtc.gni")
rtc_desktop_capture_supported = is_win || is_mac || is_linux

View File

@ -141,6 +141,8 @@
}],
['OS!="ios" ', {
'sources': [
'desktop_capturer_differ_wrapper.cc',
'desktop_capturer_differ_wrapper.h',
'differ_block.cc',
'differ_block.h',
'screen_capturer_differ_wrapper.cc',

View File

@ -11,7 +11,7 @@
#include "webrtc/modules/desktop_capture/desktop_capturer.h"
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/screen_capturer_differ_wrapper.h"
#include "webrtc/modules/desktop_capture/desktop_capturer_differ_wrapper.h"
namespace webrtc {
@ -34,4 +34,32 @@ bool DesktopCapturer::FocusOnSelectedSource() {
return false;
}
// Some platforms, such as ChromeOS, have their DesktopCapturer implementations
// in Chromium. So even we do not have CreateRawWindowCapturer() or
// CreateRawScreenCapturer() implemented in WebRTC, we still need to build other
// DesktopCapturer components when RTC_DESKTOP_CAPTURE_SUPPORTED is not defined.
#if defined(RTC_DESKTOP_CAPTURE_SUPPORTED)
// static
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateWindowCapturer(
const DesktopCaptureOptions& options) {
std::unique_ptr<DesktopCapturer> capturer = CreateRawWindowCapturer(options);
if (options.detect_updated_region()) {
capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
}
return capturer;
}
// static
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateScreenCapturer(
const DesktopCaptureOptions& options) {
std::unique_ptr<DesktopCapturer> capturer = CreateRawScreenCapturer(options);
if (options.detect_updated_region()) {
capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
}
return capturer;
}
#endif // defined(RTC_DESKTOP_CAPTURE_SUPPORTED)
} // namespace webrtc

View File

@ -110,6 +110,28 @@ class DesktopCapturer {
// Returns false in case of a failure or no source has been selected or the
// implementation does not support this functionality.
virtual bool FocusOnSelectedSource();
// Creates a DesktopCapturer instance which targets to capture windows.
static std::unique_ptr<DesktopCapturer> CreateWindowCapturer(
const DesktopCaptureOptions& options);
// Creates a DesktopCapturer instance which targets to capture screens.
static std::unique_ptr<DesktopCapturer> CreateScreenCapturer(
const DesktopCaptureOptions& options);
protected:
// CroppingWindowCapturer needs to create raw capturers without wrappers, so
// the following two functions are protected.
// Creates a platform specific DesktopCapturer instance which targets to
// capture windows.
static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
const DesktopCaptureOptions& options);
// Creates a platform specific DesktopCapturer instance which targets to
// capture screens.
static std::unique_ptr<DesktopCapturer> CreateRawScreenCapturer(
const DesktopCaptureOptions& options);
};
} // namespace webrtc

View File

@ -0,0 +1,220 @@
/*
* Copyright (c) 2016 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/desktop_capturer_differ_wrapper.h"
#include <string.h>
#include <algorithm>
#include <utility>
#include "webrtc/base/checks.h"
#include "webrtc/base/timeutils.h"
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
#include "webrtc/modules/desktop_capture/differ_block.h"
namespace webrtc {
namespace {
// Returns true if (0, 0) - (|width|, |height|) vector in |old_buffer| and
// |new_buffer| are equal. |width| should be less than 32
// (defined by kBlockSize), otherwise BlockDifference() should be used.
bool PartialBlockDifference(const uint8_t* old_buffer,
const uint8_t* new_buffer,
int width,
int height,
int stride) {
RTC_DCHECK_LT(width, kBlockSize);
const int width_bytes = width * DesktopFrame::kBytesPerPixel;
for (int i = 0; i < height; i++) {
if (memcmp(old_buffer, new_buffer, width_bytes) != 0) {
return true;
}
old_buffer += stride;
new_buffer += stride;
}
return false;
}
// Compares columns in the range of [|left|, |right|), in a row in the
// range of [|top|, |top| + |height|), starts from |old_buffer| and
// |new_buffer|, and outputs updated regions into |output|. |stride| is the
// DesktopFrame::stride().
void CompareRow(const uint8_t* old_buffer,
const uint8_t* new_buffer,
const int left,
const int right,
const int top,
const int bottom,
const int stride,
DesktopRegion* const output) {
const int block_x_offset = kBlockSize * DesktopFrame::kBytesPerPixel;
const int width = right - left;
const int height = bottom - top;
const int block_count = (width - 1) / kBlockSize;
const int last_block_width = width - block_count * kBlockSize;
RTC_DCHECK(last_block_width <= kBlockSize && last_block_width > 0);
// The first block-column in a continuous dirty area in current block-row.
int first_dirty_x_block = -1;
// We always need to add dirty area into |output| in the last block, so handle
// it separatedly.
for (int x = 0; x < block_count; x++) {
if (BlockDifference(old_buffer, new_buffer, height, stride)) {
if (first_dirty_x_block == -1) {
// This is the first dirty block in a continuous dirty area.
first_dirty_x_block = x;
}
} else if (first_dirty_x_block != -1) {
// The block on the left is the last dirty block in a continuous
// dirty area.
output->AddRect(
DesktopRect::MakeLTRB(first_dirty_x_block * kBlockSize + left, top,
x * kBlockSize + left, bottom));
first_dirty_x_block = -1;
}
old_buffer += block_x_offset;
new_buffer += block_x_offset;
}
bool last_block_diff;
if (last_block_width < kBlockSize) {
// The last one is a partial vector.
last_block_diff = PartialBlockDifference(old_buffer, new_buffer,
last_block_width, height, stride);
} else {
last_block_diff = BlockDifference(old_buffer, new_buffer, height, stride);
}
if (last_block_diff) {
if (first_dirty_x_block == -1) {
first_dirty_x_block = block_count;
}
output->AddRect(DesktopRect::MakeLTRB(
first_dirty_x_block * kBlockSize + left, top, right, bottom));
} else if (first_dirty_x_block != -1) {
output->AddRect(
DesktopRect::MakeLTRB(first_dirty_x_block * kBlockSize + left, top,
block_count * kBlockSize + left, bottom));
}
}
// Compares |rect| area in |old_frame| and |new_frame|, and outputs dirty
// regions into |output|.
void CompareFrames(const DesktopFrame& old_frame,
const DesktopFrame& new_frame,
DesktopRect rect,
DesktopRegion* const output) {
RTC_DCHECK(old_frame.size().equals(new_frame.size()));
RTC_DCHECK_EQ(old_frame.stride(), new_frame.stride());
rect.IntersectWith(DesktopRect::MakeSize(old_frame.size()));
const int y_block_count = (rect.height() - 1) / kBlockSize;
const int last_y_block_height = rect.height() - y_block_count * kBlockSize;
// Offset from the start of one block-row to the next.
const int block_y_stride = old_frame.stride() * kBlockSize;
const uint8_t* prev_block_row_start =
old_frame.GetFrameDataAtPos(rect.top_left());
const uint8_t* curr_block_row_start =
new_frame.GetFrameDataAtPos(rect.top_left());
int top = rect.top();
// The last row may have a different height, so we handle it separately.
for (int y = 0; y < y_block_count; y++) {
CompareRow(prev_block_row_start, curr_block_row_start, rect.left(),
rect.right(), top, top + kBlockSize, old_frame.stride(), output);
top += kBlockSize;
prev_block_row_start += block_y_stride;
curr_block_row_start += block_y_stride;
}
CompareRow(prev_block_row_start, curr_block_row_start, rect.left(),
rect.right(), top, top + last_y_block_height, old_frame.stride(),
output);
}
} // namespace
DesktopCapturerDifferWrapper::DesktopCapturerDifferWrapper(
std::unique_ptr<DesktopCapturer> base_capturer)
: base_capturer_(std::move(base_capturer)) {
RTC_DCHECK(base_capturer_);
}
DesktopCapturerDifferWrapper::~DesktopCapturerDifferWrapper() {}
void DesktopCapturerDifferWrapper::Start(DesktopCapturer::Callback* callback) {
callback_ = callback;
base_capturer_->Start(this);
}
void DesktopCapturerDifferWrapper::SetSharedMemoryFactory(
std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
base_capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory));
}
void DesktopCapturerDifferWrapper::CaptureFrame() {
base_capturer_->CaptureFrame();
}
void DesktopCapturerDifferWrapper::SetExcludedWindow(WindowId window) {
base_capturer_->SetExcludedWindow(window);
}
bool DesktopCapturerDifferWrapper::GetSourceList(SourceList* sources) {
return base_capturer_->GetSourceList(sources);
}
bool DesktopCapturerDifferWrapper::SelectSource(SourceId id) {
return base_capturer_->SelectSource(id);
}
bool DesktopCapturerDifferWrapper::FocusOnSelectedSource() {
return base_capturer_->FocusOnSelectedSource();
}
void DesktopCapturerDifferWrapper::OnCaptureResult(
Result result,
std::unique_ptr<DesktopFrame> input_frame) {
int64_t start_time_nanos = rtc::TimeNanos();
if (!input_frame) {
callback_->OnCaptureResult(result, nullptr);
return;
}
RTC_DCHECK(result == Result::SUCCESS);
std::unique_ptr<SharedDesktopFrame> frame =
SharedDesktopFrame::Wrap(std::move(input_frame));
if (last_frame_ && (last_frame_->size().width() != frame->size().width() ||
last_frame_->size().height() != frame->size().height() ||
last_frame_->stride() != frame->stride())) {
last_frame_.reset();
}
if (last_frame_) {
DesktopRegion hints;
hints.Swap(frame->GetUnderlyingFrame()->mutable_updated_region());
for (DesktopRegion::Iterator it(hints); !it.IsAtEnd(); it.Advance()) {
CompareFrames(*last_frame_, *frame, it.rect(),
frame->mutable_updated_region());
}
} else {
frame->mutable_updated_region()->SetRect(
DesktopRect::MakeSize(frame->size()));
}
last_frame_ = frame->Share();
frame->set_capture_time_ms(frame->GetUnderlyingFrame()->capture_time_ms() +
(rtc::TimeNanos() - start_time_nanos) /
rtc::kNumNanosecsPerMillisec);
callback_->OnCaptureResult(result, std::move(frame));
}
} // namespace webrtc

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2016 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_DESKTOP_CAPTURER_DIFFER_WRAPPER_H_
#define WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_DIFFER_WRAPPER_H_
#include <memory>
#include "webrtc/modules/desktop_capture/desktop_capturer.h"
#include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
namespace webrtc {
// DesktopCapturer wrapper that calculates updated_region() by comparing frames
// content. This class always expects the underlying DesktopCapturer
// implementation returns a superset of updated regions in DestkopFrame. If a
// DesktopCapturer implementation does not know the updated region, it should
// set updated_region() to full frame.
//
// This class marks entire frame as updated if the frame size or frame stride
// has been changed.
class DesktopCapturerDifferWrapper : public DesktopCapturer,
public DesktopCapturer::Callback {
public:
// Creates a DesktopCapturerDifferWrapper with a DesktopCapturer
// implementation, and takes its ownership.
explicit DesktopCapturerDifferWrapper(
std::unique_ptr<DesktopCapturer> base_capturer);
~DesktopCapturerDifferWrapper() 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* screens) override;
bool SelectSource(SourceId id) override;
bool FocusOnSelectedSource() override;
private:
// DesktopCapturer::Callback interface.
void OnCaptureResult(Result result,
std::unique_ptr<DesktopFrame> frame) override;
const std::unique_ptr<DesktopCapturer> base_capturer_;
DesktopCapturer::Callback* callback_;
std::unique_ptr<SharedDesktopFrame> last_frame_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_DIFFER_WRAPPER_H_

View File

@ -0,0 +1,291 @@
/*
* Copyright (c) 2016 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/desktop_capturer_differ_wrapper.h"
#include <initializer_list>
#include <memory>
#include <utility>
#include <vector>
#include "webrtc/base/random.h"
#include "webrtc/base/timeutils.h"
#include "webrtc/modules/desktop_capture/desktop_geometry.h"
#include "webrtc/modules/desktop_capture/desktop_region.h"
#include "webrtc/modules/desktop_capture/differ_block.h"
#include "webrtc/modules/desktop_capture/fake_screen_capturer.h"
#include "webrtc/modules/desktop_capture/mock_desktop_capturer_callback.h"
#include "webrtc/system_wrappers/include/cpu_features_wrapper.h"
#include "webrtc/test/gtest.h"
#include "webrtc/typedefs.h"
namespace webrtc {
namespace {
// Compares and asserts |frame|.updated_region() equals to |rects|. This
// function does not care about the order of the |rects| and it does not expect
// DesktopRegion to return an exact area for each rectangle in |rects|.
template <template <typename, typename...> class T = std::initializer_list,
typename... Rect>
void AssertUpdatedRegionIs(const DesktopFrame& frame,
const T<DesktopRect, Rect...>& rects) {
DesktopRegion region;
for (const auto& rect : rects) {
region.AddRect(rect);
}
ASSERT_TRUE(frame.updated_region().Equals(region));
}
// Compares and asserts |frame|.updated_region() covers all rectangles in
// |rects|, but does not cover areas other than a kBlockSize expansion. This
// function does not care about the order of the |rects|, and it does not expect
// DesktopRegion to return an exact area of each rectangle in |rects|.
template <template <typename, typename...> class T = std::initializer_list,
typename... Rect>
void AssertUpdatedRegionCovers(const DesktopFrame& frame,
const T<DesktopRect, Rect...>& rects) {
DesktopRegion region;
for (const auto& rect : rects) {
region.AddRect(rect);
}
// Intersect of |rects| and |frame|.updated_region() should be |rects|. i.e.
// |frame|.updated_region() should be a superset of |rects|.
DesktopRegion intersect(region);
intersect.IntersectWith(frame.updated_region());
ASSERT_TRUE(region.Equals(intersect));
// Difference between |rects| and |frame|.updated_region() should not cover
// areas which have larger than twice of kBlockSize width and height.
//
// Explanation of the 'twice' of kBlockSize (indeed kBlockSize * 2 - 2) is
// following,
// (Each block in the following grid is a 8 x 8 pixels area. X means the real
// updated area, m means the updated area marked by
// DesktopCapturerDifferWrapper.)
// +---+---+---+---+---+---+---+---+
// | X | m | m | m | m | m | m | m |
// +---+---+---+---+---+---+---+---+
// | m | m | m | m | m | m | m | m |
// +---+---+---+---+---+---+---+---+
// | m | m | m | m | m | m | m | m |
// +---+---+---+---+---+---+---+---+
// | m | m | m | m | m | m | m | X |
// +---+---+---+---+---+---+---+---+
// The top left [0, 0] - [8, 8] and right bottom [56, 24] - [64, 32] blocks of
// this area are updated. But since DesktopCapturerDifferWrapper compares
// 32 x 32 blocks by default, this entire area is marked as updated. So the
// [8, 8] - [56, 32] is expected to be covered in the difference.
//
// But if [0, 0] - [8, 8] and [64, 24] - [72, 32] blocks are updated,
// +---+---+---+---+---+---+---+---+---+---+---+---+
// | X | m | m | m | | | | | m | m | m | m |
// +---+---+---+---+---+---+---+---+---+---+---+---+
// | m | m | m | m | | | | | m | m | m | m |
// +---+---+---+---+---+---+---+---+---+---+---+---+
// | m | m | m | m | | | | | m | m | m | m |
// +---+---+---+---+---+---+---+---+---+---+---+---+
// | m | m | m | m | | | | | X | m | m | m |
// +---+---+---+---+---+---+---+---+---+---+---+---+
// the [8, 8] - [64, 32] is not expected to be covered in the difference. As
// DesktopCapturerDifferWrapper should only mark [0, 0] - [32, 32] and
// [64, 0] - [96, 32] as updated.
DesktopRegion differ(frame.updated_region());
differ.Subtract(region);
for (DesktopRegion::Iterator it(differ); !it.IsAtEnd(); it.Advance()) {
ASSERT_TRUE(it.rect().width() <= kBlockSize * 2 - 2 ||
it.rect().height() <= kBlockSize * 2 - 2);
}
}
// Executes a DesktopCapturerDifferWrapper::Capture() and compares its output
// DesktopFrame::updated_region() with |updated_region| if |check_result| is
// true. If |exactly_match| is true, AssertUpdatedRegionIs() will be used,
// otherwise AssertUpdatedRegionCovers() will be used.
template <template <typename, typename...> class T = std::initializer_list,
typename... Rect>
void ExecuteDifferWrapperCase(BlackWhiteDesktopFramePainter* frame_painter,
DesktopCapturerDifferWrapper* capturer,
MockDesktopCapturerCallback* callback,
const T<DesktopRect, Rect...>& updated_region,
bool check_result,
bool exactly_match) {
EXPECT_CALL(*callback,
OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, testing::_))
.Times(1)
.WillOnce(testing::Invoke([&updated_region, check_result, exactly_match](
DesktopCapturer::Result result,
std::unique_ptr<DesktopFrame>* frame) {
ASSERT_EQ(result, DesktopCapturer::Result::SUCCESS);
if (check_result) {
if (exactly_match) {
AssertUpdatedRegionIs(**frame, updated_region);
} else {
AssertUpdatedRegionCovers(**frame, updated_region);
}
}
}));
for (const auto& rect : updated_region) {
frame_painter->updated_region()->AddRect(rect);
}
capturer->CaptureFrame();
}
// Executes a DesktopCapturerDifferWrapper::Capture(), if updated_region() is
// not set, this function will reset DesktopCapturerDifferWrapper internal
// DesktopFrame into black.
void ExecuteCapturer(DesktopCapturerDifferWrapper* capturer,
MockDesktopCapturerCallback* callback) {
EXPECT_CALL(*callback,
OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, testing::_))
.Times(1);
capturer->CaptureFrame();
}
void ExecuteDifferWrapperTest(bool with_hints,
bool enlarge_updated_region,
bool random_updated_region,
bool check_result) {
const bool updated_region_should_exactly_match =
with_hints && !enlarge_updated_region && !random_updated_region;
BlackWhiteDesktopFramePainter frame_painter;
PainterDesktopFrameGenerator frame_generator;
frame_generator.set_desktop_frame_painter(&frame_painter);
std::unique_ptr<FakeDesktopCapturer<>> fake(new FakeDesktopCapturer<>());
fake->set_frame_generator(&frame_generator);
DesktopCapturerDifferWrapper capturer(std::move(fake));
MockDesktopCapturerCallback callback;
frame_generator.set_provide_updated_region_hints(with_hints);
frame_generator.set_enlarge_updated_region(enlarge_updated_region);
frame_generator.set_add_random_updated_region(random_updated_region);
capturer.Start(&callback);
EXPECT_CALL(callback,
OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, testing::_))
.Times(1)
.WillOnce(testing::Invoke([](DesktopCapturer::Result result,
std::unique_ptr<DesktopFrame>* frame) {
ASSERT_EQ(result, DesktopCapturer::Result::SUCCESS);
AssertUpdatedRegionIs(**frame,
{DesktopRect::MakeSize((*frame)->size())});
}));
capturer.CaptureFrame();
ExecuteDifferWrapperCase(&frame_painter, &capturer, &callback,
{DesktopRect::MakeLTRB(100, 100, 200, 200),
DesktopRect::MakeLTRB(300, 300, 400, 400)},
check_result, updated_region_should_exactly_match);
ExecuteCapturer(&capturer, &callback);
ExecuteDifferWrapperCase(
&frame_painter, &capturer, &callback,
{DesktopRect::MakeLTRB(0, 0, 40, 40),
DesktopRect::MakeLTRB(0, frame_generator.size()->height() - 40, 40,
frame_generator.size()->height()),
DesktopRect::MakeLTRB(frame_generator.size()->width() - 40, 0,
frame_generator.size()->width(), 40),
DesktopRect::MakeLTRB(frame_generator.size()->width() - 40,
frame_generator.size()->height() - 40,
frame_generator.size()->width(),
frame_generator.size()->height())},
check_result, updated_region_should_exactly_match);
Random random(rtc::TimeMillis());
// Fuzzing tests.
for (int i = 0; i < 1000; i++) {
if (enlarge_updated_region) {
frame_generator.set_enlarge_range(random.Rand(1, 50));
}
frame_generator.size()->set(random.Rand(500, 2000), random.Rand(500, 2000));
ExecuteCapturer(&capturer, &callback);
std::vector<DesktopRect> updated_region;
for (int j = random.Rand(50); j >= 0; j--) {
// At least a 1 x 1 updated region.
const int left = random.Rand(0, frame_generator.size()->width() - 2);
const int top = random.Rand(0, frame_generator.size()->height() - 2);
const int right = random.Rand(left + 1, frame_generator.size()->width());
const int bottom = random.Rand(top + 1, frame_generator.size()->height());
updated_region.push_back(DesktopRect::MakeLTRB(left, top, right, bottom));
}
ExecuteDifferWrapperCase(&frame_painter, &capturer, &callback,
updated_region, check_result,
updated_region_should_exactly_match);
}
}
} // namespace
TEST(DesktopCapturerDifferWrapperTest, CaptureWithoutHints) {
ExecuteDifferWrapperTest(false, false, false, true);
}
TEST(DesktopCapturerDifferWrapperTest, CaptureWithHints) {
ExecuteDifferWrapperTest(true, false, false, true);
}
TEST(DesktopCapturerDifferWrapperTest, CaptureWithEnlargedHints) {
ExecuteDifferWrapperTest(true, true, false, true);
}
TEST(DesktopCapturerDifferWrapperTest, CaptureWithRandomHints) {
ExecuteDifferWrapperTest(true, false, true, true);
}
TEST(DesktopCapturerDifferWrapperTest, CaptureWithEnlargedAndRandomHints) {
ExecuteDifferWrapperTest(true, true, true, true);
}
// When hints are provided, DesktopCapturerDifferWrapper has a slightly better
// performance in current configuration, but not so significant. Following is
// one run result.
// [ RUN ] DISABLED_CaptureWithoutHintsPerf
// [ OK ] DISABLED_CaptureWithoutHintsPerf (7118 ms)
// [ RUN ] DISABLED_CaptureWithHintsPerf
// [ OK ] DISABLED_CaptureWithHintsPerf (5580 ms)
// [ RUN ] DISABLED_CaptureWithEnlargedHintsPerf
// [ OK ] DISABLED_CaptureWithEnlargedHintsPerf (5974 ms)
// [ RUN ] DISABLED_CaptureWithRandomHintsPerf
// [ OK ] DISABLED_CaptureWithRandomHintsPerf (6184 ms)
// [ RUN ] DISABLED_CaptureWithEnlargedAndRandomHintsPerf
// [ OK ] DISABLED_CaptureWithEnlargedAndRandomHintsPerf (6347 ms)
TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithoutHintsPerf) {
int64_t started = rtc::TimeMillis();
ExecuteDifferWrapperTest(false, false, false, false);
ASSERT_LE(rtc::TimeMillis() - started, 15000);
}
TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithHintsPerf) {
int64_t started = rtc::TimeMillis();
ExecuteDifferWrapperTest(true, false, false, false);
ASSERT_LE(rtc::TimeMillis() - started, 15000);
}
TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithEnlargedHintsPerf) {
int64_t started = rtc::TimeMillis();
ExecuteDifferWrapperTest(true, true, false, false);
ASSERT_LE(rtc::TimeMillis() - started, 15000);
}
TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithRandomHintsPerf) {
int64_t started = rtc::TimeMillis();
ExecuteDifferWrapperTest(true, false, true, false);
ASSERT_LE(rtc::TimeMillis() - started, 15000);
}
TEST(DesktopCapturerDifferWrapperTest,
DISABLED_CaptureWithEnlargedAndRandomHintsPerf) {
int64_t started = rtc::TimeMillis();
ExecuteDifferWrapperTest(true, true, true, false);
ASSERT_LE(rtc::TimeMillis() - started, 15000);
}
} // namespace webrtc

View File

@ -15,6 +15,7 @@
#include <utility>
#include "webrtc/modules/desktop_capture/desktop_capturer.h"
#include "webrtc/modules/desktop_capture/desktop_capture_types.h"
#include "webrtc/modules/desktop_capture/desktop_frame_generator.h"
#include "webrtc/modules/desktop_capture/shared_desktop_frame.h"
#include "webrtc/modules/desktop_capture/shared_memory.h"
@ -37,7 +38,7 @@ namespace webrtc {
//
// TODO(zijiehe): Remove template T once we merge ScreenCapturer and
// WindowCapturer.
template <typename T>
template <typename T = DesktopCapturer>
class FakeDesktopCapturer : public T {
public:
FakeDesktopCapturer()
@ -83,7 +84,20 @@ class FakeDesktopCapturer : public T {
shared_memory_factory_ = std::move(shared_memory_factory);
}
bool GetSourceList(DesktopCapturer::SourceList* sources) override {
sources->push_back({kWindowId, "A-Fake-DesktopCapturer-Window"});
sources->push_back({kScreenId});
return true;
}
bool SelectSource(DesktopCapturer::SourceId id) override {
return id == kWindowId || id == kScreenId || id == kFullDesktopScreenId;
}
private:
static constexpr DesktopCapturer::SourceId kWindowId = 1378277495;
static constexpr DesktopCapturer::SourceId kScreenId = 1378277496;
DesktopCapturer::Callback* callback_;
std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
DesktopCapturer::Result result_;

View File

@ -0,0 +1,20 @@
/* Copyright (c) 2016 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_MOCK_DESKTOP_CAPTURER_CALLBACK_H_
#define WEBRTC_MODULES_DESKTOP_CAPTURE_MOCK_DESKTOP_CAPTURER_CALLBACK_H_
#include "webrtc/modules/desktop_capture/screen_capturer_mock_objects.h"
namespace webrtc {
// TODO(zijiehe): Remove MockScreenCapturerCallback.
class MockDesktopCapturerCallback : public MockScreenCapturerCallback {};
} // namespace webrtc
#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_MOCK_DESKTOP_CAPTURER_CALLBACK_H_

View File

@ -18,6 +18,7 @@
namespace webrtc {
// TODO(zijiehe): Remove, use DesktopCapturerDifferWrapper.
// ScreenCapturer wrapper that calculates updated_region() by comparing frames
// content. This class always expects the underlying ScreenCapturer
// implementation returns a superset of updated regions in DestkopFrame. If a

View File

@ -26,7 +26,7 @@
#include "webrtc/modules/desktop_capture/desktop_capture_options.h"
#include "webrtc/modules/desktop_capture/desktop_frame.h"
#include "webrtc/modules/desktop_capture/desktop_region.h"
#include "webrtc/modules/desktop_capture/screen_capturer_mock_objects.h"
#include "webrtc/modules/desktop_capture/mock_desktop_capturer_callback.h"
#include "webrtc/modules/desktop_capture/screen_drawer.h"
#if defined(WEBRTC_WIN)
@ -77,16 +77,16 @@ bool ArePixelsColoredBy(const DesktopFrame& frame,
class ScreenCapturerIntegrationTest : public testing::Test {
public:
void SetUp() override {
capturer_.reset(
ScreenCapturer::Create(DesktopCaptureOptions::CreateDefault()));
capturer_ = DesktopCapturer::CreateScreenCapturer(
DesktopCaptureOptions::CreateDefault());
}
protected:
void TestCaptureUpdatedRegion(
std::initializer_list<ScreenCapturer*> capturers) {
std::initializer_list<DesktopCapturer*> capturers) {
RTC_DCHECK(capturers.size() > 0);
// A large enough area for the tests, which should be able to fulfill by
// most of systems.
// A large enough area for the tests, which should be able to be fulfilled
// by most systems.
const int kTestArea = 512;
const int kRectSize = 32;
std::unique_ptr<ScreenDrawer> drawer = ScreenDrawer::Create();
@ -101,7 +101,7 @@ class ScreenCapturerIntegrationTest : public testing::Test {
return;
}
for (ScreenCapturer* capturer : capturers) {
for (DesktopCapturer* capturer : capturers) {
capturer->Start(&callback_);
}
@ -138,11 +138,12 @@ class ScreenCapturerIntegrationTest : public testing::Test {
#if defined(WEBRTC_WIN)
// Enable allow_directx_capturer in DesktopCaptureOptions, but let
// ScreenCapturer::Create to decide whether a DirectX capturer should be used.
// DesktopCapturer::CreateScreenCapturer() to decide whether a DirectX
// capturer should be used.
void MaybeCreateDirectxCapturer() {
DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault());
options.set_allow_directx_capturer(true);
capturer_.reset(ScreenCapturer::Create(options));
capturer_ = DesktopCapturer::CreateScreenCapturer(options);
}
bool CreateDirectxCapturer() {
@ -158,19 +159,19 @@ class ScreenCapturerIntegrationTest : public testing::Test {
void CreateMagnifierCapturer() {
DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault());
options.set_allow_use_magnification_api(true);
capturer_.reset(ScreenCapturer::Create(options));
capturer_ = DesktopCapturer::CreateScreenCapturer(options);
}
#endif // defined(WEBRTC_WIN)
std::unique_ptr<ScreenCapturer> capturer_;
MockScreenCapturerCallback callback_;
std::unique_ptr<DesktopCapturer> capturer_;
MockDesktopCapturerCallback callback_;
private:
// Repeats capturing the frame by using |capturers| one-by-one for 600 times,
// typically 30 seconds, until they succeeded captured a |color| rectangle at
// |rect|. This function uses |drawer|->WaitForPendingDraws() between two
// attempts to wait for the screen to update.
void TestCaptureOneFrame(std::vector<ScreenCapturer*> capturers,
void TestCaptureOneFrame(std::vector<DesktopCapturer*> capturers,
ScreenDrawer* drawer,
DesktopRect rect,
RgbaColor color) {
@ -182,7 +183,7 @@ class ScreenCapturerIntegrationTest : public testing::Test {
drawer->WaitForPendingDraws();
for (size_t j = 0; j < capturers.size(); j++) {
if (capturers[j] == nullptr) {
// ScreenCapturer should return an empty updated_region() if no
// DesktopCapturer should return an empty updated_region() if no
// update detected. So we won't test it again if it has captured
// the rectangle we drew.
continue;
@ -210,7 +211,7 @@ class ScreenCapturerIntegrationTest : public testing::Test {
}
// Expects |capturer| to successfully capture a frame, and returns it.
std::unique_ptr<DesktopFrame> CaptureFrame(ScreenCapturer* capturer) {
std::unique_ptr<DesktopFrame> CaptureFrame(DesktopCapturer* capturer) {
std::unique_ptr<DesktopFrame> frame;
EXPECT_CALL(callback_,
OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _))
@ -226,7 +227,7 @@ TEST_F(ScreenCapturerIntegrationTest, CaptureUpdatedRegion) {
}
TEST_F(ScreenCapturerIntegrationTest, TwoCapturers) {
std::unique_ptr<ScreenCapturer> capturer2 = std::move(capturer_);
std::unique_ptr<DesktopCapturer> capturer2 = std::move(capturer_);
SetUp();
TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()});
}
@ -246,7 +247,7 @@ TEST_F(ScreenCapturerIntegrationTest, TwoDirectxCapturers) {
return;
}
std::unique_ptr<ScreenCapturer> capturer2 = std::move(capturer_);
std::unique_ptr<DesktopCapturer> capturer2 = std::move(capturer_);
RTC_CHECK(CreateDirectxCapturer());
TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()});
}
@ -259,7 +260,7 @@ TEST_F(ScreenCapturerIntegrationTest,
TEST_F(ScreenCapturerIntegrationTest, TwoMagnifierCapturers) {
CreateMagnifierCapturer();
std::unique_ptr<ScreenCapturer> capturer2 = std::move(capturer_);
std::unique_ptr<DesktopCapturer> capturer2 = std::move(capturer_);
CreateMagnifierCapturer();
TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()});
}

View File

@ -1037,4 +1037,19 @@ ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
return capturer.release();
}
// static
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
const DesktopCaptureOptions& options) {
if (!options.configuration_monitor())
return nullptr;
std::unique_ptr<ScreenCapturer> capturer(
new ScreenCapturerMac(options.configuration_monitor()));
if (!static_cast<ScreenCapturerMac*>(capturer.get())->Init()) {
return nullptr;
}
return capturer;
}
} // namespace webrtc

View File

@ -17,20 +17,8 @@
namespace webrtc {
class MockScreenCapturer : public ScreenCapturer {
public:
MockScreenCapturer() {}
virtual ~MockScreenCapturer() {}
MOCK_METHOD1(Start, void(Callback* callback));
MOCK_METHOD0(CaptureFrame, void(void));
MOCK_METHOD1(GetScreenList, bool(ScreenList* screens));
MOCK_METHOD1(SelectScreen, bool(ScreenId id));
private:
RTC_DISALLOW_COPY_AND_ASSIGN(MockScreenCapturer);
};
// Deprecated.
// Use webrtc/modules/desktop_capture/mock_desktop_capturer_callback.h.
class MockScreenCapturerCallback : public ScreenCapturer::Callback {
public:
MockScreenCapturerCallback() {}

View File

@ -42,4 +42,22 @@ ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
return capturer.release();
}
// static
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
const DesktopCaptureOptions& options) {
std::unique_ptr<DesktopCapturer> capturer;
if (options.allow_directx_capturer() &&
ScreenCapturerWinDirectx::IsSupported()) {
capturer.reset(new ScreenCapturerWinDirectx(options));
} else {
capturer.reset(new ScreenCapturerWinGdi(options));
}
if (options.allow_use_magnification_api()) {
capturer.reset(new ScreenCapturerWinMagnifier(std::move(capturer)));
}
return capturer;
}
} // namespace webrtc

View File

@ -424,4 +424,18 @@ ScreenCapturer* ScreenCapturer::Create(const DesktopCaptureOptions& options) {
return capturer.release();
}
// static
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
const DesktopCaptureOptions& options) {
if (!options.x_display())
return nullptr;
std::unique_ptr<ScreenCapturer> capturer(new ScreenCapturerLinux());
if (!static_cast<ScreenCapturerLinux*>(capturer.get())->Init(options)) {
return nullptr;
}
return capturer;
}
} // namespace webrtc

View File

@ -36,7 +36,7 @@ static LPCTSTR kMagnifierWindowName = L"MagnifierWindow";
Atomic32 ScreenCapturerWinMagnifier::tls_index_(TLS_OUT_OF_INDEXES);
ScreenCapturerWinMagnifier::ScreenCapturerWinMagnifier(
std::unique_ptr<ScreenCapturer> fallback_capturer)
std::unique_ptr<DesktopCapturer> fallback_capturer)
: fallback_capturer_(std::move(fallback_capturer)) {}
ScreenCapturerWinMagnifier::~ScreenCapturerWinMagnifier() {
@ -139,7 +139,7 @@ bool ScreenCapturerWinMagnifier::SelectScreen(ScreenId id) {
current_screen_id_ = id;
if (fallback_capturer_started_)
fallback_capturer_->SelectScreen(id);
fallback_capturer_->SelectSource(id);
return valid;
}
@ -382,7 +382,7 @@ void ScreenCapturerWinMagnifier::StartFallbackCapturer() {
fallback_capturer_started_ = true;
fallback_capturer_->Start(callback_);
fallback_capturer_->SelectScreen(current_screen_id_);
fallback_capturer_->SelectSource(current_screen_id_);
}
}

View File

@ -44,7 +44,7 @@ class ScreenCapturerWinMagnifier : public ScreenCapturer {
// screen is being captured, or the OS does not support Magnification API, or
// the magnifier capturer fails (e.g. in Windows8 Metro mode).
explicit ScreenCapturerWinMagnifier(
std::unique_ptr<ScreenCapturer> fallback_capturer);
std::unique_ptr<DesktopCapturer> fallback_capturer);
~ScreenCapturerWinMagnifier() override;
// Overridden from ScreenCapturer:
@ -108,7 +108,7 @@ class ScreenCapturerWinMagnifier : public ScreenCapturer {
static Atomic32 tls_index_;
std::unique_ptr<ScreenCapturer> fallback_capturer_;
std::unique_ptr<DesktopCapturer> fallback_capturer_;
bool fallback_capturer_started_ = false;
Callback* callback_ = nullptr;
std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;

View File

@ -241,4 +241,11 @@ WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
return new WindowCapturerMac(options.full_screen_chrome_window_detector());
}
// static
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
const DesktopCaptureOptions& options) {
return std::unique_ptr<DesktopCapturer>(
new WindowCapturerMac(options.full_screen_chrome_window_detector()));
}
} // namespace webrtc

View File

@ -270,4 +270,10 @@ WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
return new WindowCapturerWin();
}
// static
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
const DesktopCaptureOptions& options) {
return std::unique_ptr<DesktopCapturer>(new WindowCapturerWin());
}
} // namespace webrtc

View File

@ -430,4 +430,12 @@ WindowCapturer* WindowCapturer::Create(const DesktopCaptureOptions& options) {
return new WindowCapturerLinux(options);
}
// static
std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
const DesktopCaptureOptions& options) {
if (!options.x_display())
return nullptr;
return std::unique_ptr<DesktopCapturer>(new WindowCapturerLinux(options));
}
} // namespace webrtc