From fef8653c5adcd7c5b7fc2c49ae1a0f6927ddb37f Mon Sep 17 00:00:00 2001 From: zijiehe Date: Mon, 5 Sep 2016 15:26:32 -0700 Subject: [PATCH] An early analysis shows in DirectX based capturer, Windows API returns larger dirty region than the real screen change. A similar behavior may happen on other platforms with damage notification support. So it's better to have an individual layer to handle the Differ logic, and remove capturing independent logic out of each ScreenCapturer* implementation. So this change does following things, 1. Update differ_block to handle variable height. differ_block_sse2 has been renamed to differ_vector_sse2. 2. A new ScreenCapturerDifferWrapper implementation to help set DesktopFrame::updated_region(). It uses an underlying ScreenCapturer to do the real capture work, and updates the updated region of DesktopFrame returned from OnCaptureResult function. 3. FakeDesktopCapturer and FakeScreenCapturer to generate controllable DesktopFrame by using DesktopFrameGenerator and DesktopFramePainter. 4. Test ScreenCapturerDifferWrapper by using FakeScreenCapturer. After this change, we can eventually remove all Differ logic from ScreenCapturer* implementations, and fix a potential crash bug in ScreenCapturerLinux class. It wrongly assumes previous_frame() has a same size as current_frame(). https://goo.gl/3nSqOC BUG=633802 TBR=kjellander@webrtc.org Review-Url: https://codereview.webrtc.org/2202443002 Cr-Commit-Position: refs/heads/master@{#14076} --- webrtc/modules/BUILD.gn | 6 + webrtc/modules/desktop_capture/BUILD.gn | 6 +- .../desktop_capture/desktop_capture.gypi | 7 +- .../desktop_frame_generator.cc | 179 +++++++++++ .../desktop_capture/desktop_frame_generator.h | 121 ++++++++ .../desktop_capture/desktop_geometry.cc | 10 + .../desktop_capture/desktop_geometry.h | 10 + .../modules/desktop_capture/differ_block.cc | 72 +++-- webrtc/modules/desktop_capture/differ_block.h | 11 + .../desktop_capture/differ_block_sse2.cc | 120 -------- .../desktop_capture/differ_block_sse2.h | 33 -- .../desktop_capture/differ_vector_sse2.cc | 102 ++++++ .../desktop_capture/differ_vector_sse2.h | 31 ++ .../desktop_capture/fake_desktop_capturer.h | 95 ++++++ .../desktop_capture/fake_screen_capturer.cc | 34 ++ .../desktop_capture/fake_screen_capturer.h | 35 +++ .../screen_capturer_differ_wrapper.cc | 214 +++++++++++++ .../screen_capturer_differ_wrapper.h | 58 ++++ ...screen_capturer_differ_wrapper_unittest.cc | 291 ++++++++++++++++++ webrtc/modules/modules.gyp | 6 + 20 files changed, 1252 insertions(+), 189 deletions(-) create mode 100644 webrtc/modules/desktop_capture/desktop_frame_generator.cc create mode 100644 webrtc/modules/desktop_capture/desktop_frame_generator.h delete mode 100644 webrtc/modules/desktop_capture/differ_block_sse2.cc delete mode 100644 webrtc/modules/desktop_capture/differ_block_sse2.h create mode 100644 webrtc/modules/desktop_capture/differ_vector_sse2.cc create mode 100644 webrtc/modules/desktop_capture/differ_vector_sse2.h create mode 100644 webrtc/modules/desktop_capture/fake_desktop_capturer.h create mode 100644 webrtc/modules/desktop_capture/fake_screen_capturer.cc create mode 100644 webrtc/modules/desktop_capture/fake_screen_capturer.h create mode 100644 webrtc/modules/desktop_capture/screen_capturer_differ_wrapper.cc create mode 100644 webrtc/modules/desktop_capture/screen_capturer_differ_wrapper.h create mode 100644 webrtc/modules/desktop_capture/screen_capturer_differ_wrapper_unittest.cc diff --git a/webrtc/modules/BUILD.gn b/webrtc/modules/BUILD.gn index d4e4244df4..cf3e469727 100644 --- a/webrtc/modules/BUILD.gn +++ b/webrtc/modules/BUILD.gn @@ -504,7 +504,13 @@ if (rtc_include_tests) { deps += [ "desktop_capture" ] 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/mouse_cursor_monitor_unittest.cc", + "desktop_capture/screen_capturer_differ_wrapper_unittest.cc", "desktop_capture/screen_capturer_helper_unittest.cc", "desktop_capture/screen_capturer_mac_unittest.cc", "desktop_capture/screen_capturer_mock_objects.h", diff --git a/webrtc/modules/desktop_capture/BUILD.gn b/webrtc/modules/desktop_capture/BUILD.gn index 7c2ab6cdc7..8887ba99c3 100644 --- a/webrtc/modules/desktop_capture/BUILD.gn +++ b/webrtc/modules/desktop_capture/BUILD.gn @@ -128,6 +128,8 @@ rtc_source_set("desktop_capture") { "differ.h", "differ_block.cc", "differ_block.h", + "screen_capturer_differ_wrapper.cc", + "screen_capturer_differ_wrapper.h", ] } @@ -169,8 +171,8 @@ if (use_desktop_capture_differ_sse2) { rtc_source_set("desktop_capture_differ_sse2") { visibility = [ ":*" ] sources = [ - "differ_block_sse2.cc", - "differ_block_sse2.h", + "differ_vector_sse2.cc", + "differ_vector_sse2.h", ] if (is_posix) { diff --git a/webrtc/modules/desktop_capture/desktop_capture.gypi b/webrtc/modules/desktop_capture/desktop_capture.gypi index 872e705b52..521ef2794c 100644 --- a/webrtc/modules/desktop_capture/desktop_capture.gypi +++ b/webrtc/modules/desktop_capture/desktop_capture.gypi @@ -101,7 +101,6 @@ 'window_capturer.h', 'window_capturer_mac.mm', 'window_capturer_win.cc', - ], 'conditions': [ ['OS!="ios" and (target_arch=="ia32" or target_arch=="x64")', { @@ -145,6 +144,8 @@ 'differ.h', 'differ_block.cc', 'differ_block.h', + 'screen_capturer_differ_wrapper.cc', + 'screen_capturer_differ_wrapper.h', ], }], ['OS=="mac"', { @@ -182,8 +183,8 @@ 'target_name': 'desktop_capture_differ_sse2', 'type': 'static_library', 'sources': [ - 'differ_block_sse2.cc', - 'differ_block_sse2.h', + 'differ_vector_sse2.cc', + 'differ_vector_sse2.h', ], 'conditions': [ ['os_posix==1', { diff --git a/webrtc/modules/desktop_capture/desktop_frame_generator.cc b/webrtc/modules/desktop_capture/desktop_frame_generator.cc new file mode 100644 index 0000000000..ae5c9492cb --- /dev/null +++ b/webrtc/modules/desktop_capture/desktop_frame_generator.cc @@ -0,0 +1,179 @@ +/* + * 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_frame_generator.h" + +#include +#include + +#include + +#include "webrtc/base/random.h" +#include "webrtc/base/timeutils.h" + +namespace webrtc { + +namespace { + +// Sets |updated_region| to |frame|. If |enlarge_updated_region| is +// true, this function will randomly enlarge each DesktopRect in +// |updated_region|. But the enlarged DesktopRegion won't excceed the +// frame->size(). If |add_random_updated_region| is true, several random +// rectangles will also be included in |frame|. +void SetUpdatedRegion(DesktopFrame* frame, + const DesktopRegion& updated_region, + bool enlarge_updated_region, + int enlarge_range, + bool add_random_updated_region) { + const DesktopRect screen_rect = DesktopRect::MakeSize(frame->size()); + Random random(rtc::TimeMicros()); + frame->mutable_updated_region()->Clear(); + for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd(); + it.Advance()) { + DesktopRect rect = it.rect(); + if (enlarge_updated_region && enlarge_range > 0) { + rect.Extend(random.Rand(enlarge_range), random.Rand(enlarge_range), + random.Rand(enlarge_range), random.Rand(enlarge_range)); + rect.IntersectWith(screen_rect); + } + frame->mutable_updated_region()->AddRect(rect); + } + + if (add_random_updated_region) { + for (int i = random.Rand(10); i >= 0; i--) { + // At least a 1 x 1 updated region. + const int left = random.Rand(0, frame->size().width() - 2); + const int top = random.Rand(0, frame->size().height() - 2); + const int right = random.Rand(left + 1, frame->size().width()); + const int bottom = random.Rand(top + 1, frame->size().height()); + frame->mutable_updated_region()->AddRect( + DesktopRect::MakeLTRB(left, top, right, bottom)); + } + } +} + +// Paints pixels in |rect| of |frame| to |color|. +void PaintRect(DesktopFrame* frame, DesktopRect rect, uint32_t color) { + static_assert(DesktopFrame::kBytesPerPixel == sizeof(uint32_t), + "kBytesPerPixel should be 4."); + RTC_DCHECK(frame->size().width() >= rect.right() && + frame->size().height() >= rect.bottom()); + uint8_t* row = frame->GetFrameDataAtPos(rect.top_left()); + for (int i = 0; i < rect.height(); i++) { + uint32_t* column = reinterpret_cast(row); + for (int j = 0; j < rect.width(); j++) { + column[j] = color; + } + row += frame->stride(); + } +} + +// Paints pixels in |region| of |frame| to |color|. +void PaintRegion(DesktopFrame* frame, DesktopRegion* region, uint32_t color) { + region->IntersectWith(DesktopRect::MakeSize(frame->size())); + for (DesktopRegion::Iterator it(*region); !it.IsAtEnd(); it.Advance()) { + PaintRect(frame, it.rect(), color); + } +} + +} // namespace + +DesktopFrameGenerator::DesktopFrameGenerator() {} +DesktopFrameGenerator::~DesktopFrameGenerator() {} + +DesktopFramePainter::DesktopFramePainter() {} +DesktopFramePainter::~DesktopFramePainter() {} + +PainterDesktopFrameGenerator::PainterDesktopFrameGenerator() + : size_(1024, 768), + return_frame_(true), + provide_updated_region_hints_(false), + enlarge_updated_region_(false), + enlarge_range_(20), + add_random_updated_region_(false), + painter_(nullptr) {} +PainterDesktopFrameGenerator::~PainterDesktopFrameGenerator() {} + +std::unique_ptr PainterDesktopFrameGenerator::GetNextFrame( + SharedMemoryFactory* factory) { + if (!return_frame_) { + return nullptr; + } + + std::unique_ptr frame = std::unique_ptr( + factory ? SharedMemoryDesktopFrame::Create(size_, factory).release() + : new BasicDesktopFrame(size_)); + if (painter_) { + DesktopRegion updated_region; + if (!painter_->Paint(frame.get(), &updated_region)) { + return nullptr; + } + + if (provide_updated_region_hints_) { + SetUpdatedRegion(frame.get(), updated_region, enlarge_updated_region_, + enlarge_range_, add_random_updated_region_); + } else { + frame->mutable_updated_region()->SetRect( + DesktopRect::MakeSize(frame->size())); + } + } + + return frame; +} + +DesktopSize* PainterDesktopFrameGenerator::size() { + return &size_; +} + +void PainterDesktopFrameGenerator::set_return_frame(bool return_frame) { + return_frame_ = return_frame; +} + +void PainterDesktopFrameGenerator::set_provide_updated_region_hints( + bool provide_updated_region_hints) { + provide_updated_region_hints_ = provide_updated_region_hints; +} + +void PainterDesktopFrameGenerator::set_enlarge_updated_region( + bool enlarge_updated_region) { + enlarge_updated_region_ = enlarge_updated_region; +} + +void PainterDesktopFrameGenerator::set_enlarge_range(int enlarge_range) { + enlarge_range_ = enlarge_range; +} + +void PainterDesktopFrameGenerator::set_add_random_updated_region( + bool add_random_updated_region) { + add_random_updated_region_ = add_random_updated_region; +} + +void PainterDesktopFrameGenerator::set_desktop_frame_painter( + DesktopFramePainter* painter) { + painter_ = painter; +} + +BlackWhiteDesktopFramePainter::BlackWhiteDesktopFramePainter() {} +BlackWhiteDesktopFramePainter::~BlackWhiteDesktopFramePainter() {} + +DesktopRegion* BlackWhiteDesktopFramePainter::updated_region() { + return &updated_region_; +} + +bool BlackWhiteDesktopFramePainter::Paint(DesktopFrame* frame, + DesktopRegion* updated_region) { + RTC_DCHECK(updated_region->is_empty()); + memset(frame->data(), 0, frame->stride() * frame->size().height()); + PaintRegion(frame, &updated_region_, UINT32_MAX); + updated_region_.Swap(updated_region); + return true; +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/desktop_frame_generator.h b/webrtc/modules/desktop_capture/desktop_frame_generator.h new file mode 100644 index 0000000000..2b767dcb4d --- /dev/null +++ b/webrtc/modules/desktop_capture/desktop_frame_generator.h @@ -0,0 +1,121 @@ +/* + * 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_FRAME_GENERATOR_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_GENERATOR_H_ + +#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_memory.h" + +namespace webrtc { + +// An interface to generate a DesktopFrame. +class DesktopFrameGenerator { + public: + DesktopFrameGenerator(); + virtual ~DesktopFrameGenerator(); + + virtual std::unique_ptr GetNextFrame( + SharedMemoryFactory* factory) = 0; +}; + +// An interface to paint a DesktopFrame. This interface is used by +// PainterDesktopFrameGenerator. +class DesktopFramePainter { + public: + DesktopFramePainter(); + virtual ~DesktopFramePainter(); + + virtual bool Paint(DesktopFrame* frame, DesktopRegion* updated_region) = 0; +}; + +// An implementation of DesktopFrameGenerator to take care about the +// DesktopFrame size, filling updated_region(), etc, but leaves the real +// painting work to a DesktopFramePainter implementation. +class PainterDesktopFrameGenerator final : public DesktopFrameGenerator { + public: + PainterDesktopFrameGenerator(); + ~PainterDesktopFrameGenerator() override; + + std::unique_ptr GetNextFrame( + SharedMemoryFactory* factory) override; + + // Sets the size of the frame which will be returned in next GetNextFrame() + // call. + DesktopSize* size(); + + // Decides whether BaseDesktopFrameGenerator returns a frame in next Capture() + // callback. If return_frame_ is true, BaseDesktopFrameGenerator will create a + // frame according to both size_ and SharedMemoryFactory input, and uses + // Paint() function to paint it. + void set_return_frame(bool return_frame); + + // Decides whether MockScreenCapturer returns a frame with updated regions. + // MockScreenCapturer will keep DesktopFrame::updated_region() empty if this + // field is false. + void set_provide_updated_region_hints(bool provide_updated_region_hints); + + // Decides whether MockScreenCapturer randomly enlarges updated regions in the + // DesktopFrame. Set this field to true to simulate an inaccurate updated + // regions' return from OS APIs. + void set_enlarge_updated_region(bool enlarge_updated_region); + + // The range to enlarge a updated region if |enlarge_updated_region_| is true. + // If this field is less than zero, it will be treated as zero, and + // |enlarge_updated_region_| will be ignored. + void set_enlarge_range(int enlarge_range); + + // Decides whether BaseDesktopFrameGenerator randomly add some updated regions + // in the DesktopFrame. Set this field to true to simulate an inaccurate + // updated regions' return from OS APIs. + void set_add_random_updated_region(bool add_random_updated_region); + + // Sets the painter object to do the real painting work, if no |painter_| has + // been set to this instance, the DesktopFrame returned by GetNextFrame() + // function will keep in an undefined but valid state. + // PainterDesktopFrameGenerator does not take ownership of the |painter|. + void set_desktop_frame_painter(DesktopFramePainter* painter); + + private: + DesktopSize size_; + bool return_frame_; + bool provide_updated_region_hints_; + bool enlarge_updated_region_; + int enlarge_range_; + bool add_random_updated_region_; + DesktopFramePainter* painter_; +}; + +// An implementation of DesktopFramePainter to paint black on +// mutable_updated_region(), and white elsewhere. +class BlackWhiteDesktopFramePainter final : public DesktopFramePainter { + public: + BlackWhiteDesktopFramePainter(); + ~BlackWhiteDesktopFramePainter() override; + + // The black regions of the frame which will be returned in next Paint() + // call. BlackWhiteDesktopFramePainter will draw a white frame, with black + // in the updated_region_. Each Paint() call will consume updated_region_. + DesktopRegion* updated_region(); + + // DesktopFramePainter interface. + bool Paint(DesktopFrame* frame, DesktopRegion* updated_region) override; + + private: + DesktopRegion updated_region_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_GENERATOR_H_ diff --git a/webrtc/modules/desktop_capture/desktop_geometry.cc b/webrtc/modules/desktop_capture/desktop_geometry.cc index 1ff7c683c7..2af8cf826e 100644 --- a/webrtc/modules/desktop_capture/desktop_geometry.cc +++ b/webrtc/modules/desktop_capture/desktop_geometry.cc @@ -44,5 +44,15 @@ void DesktopRect::Translate(int32_t dx, int32_t dy) { bottom_ += dy; } +void DesktopRect::Extend(int32_t left_offset, + int32_t top_offset, + int32_t right_offset, + int32_t bottom_offset) { + left_ -= left_offset; + top_ -= top_offset; + right_ += right_offset; + bottom_ += bottom_offset; +} + } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/desktop_geometry.h b/webrtc/modules/desktop_capture/desktop_geometry.h index 047eeec3d9..8e9b9183a8 100644 --- a/webrtc/modules/desktop_capture/desktop_geometry.h +++ b/webrtc/modules/desktop_capture/desktop_geometry.h @@ -128,6 +128,16 @@ class DesktopRect { void Translate(int32_t dx, int32_t dy); void Translate(DesktopVector d) { Translate(d.x(), d.y()); }; + // Enlarges current DesktopRect by subtracting |left_offset| and |top_offset| + // from |left_| and |top_|, and adding |right_offset| and |bottom_offset| to + // |right_| and |bottom_|. This function does not normalize the result, so + // |left_| and |top_| may be less than zero or larger than |right_| and + // |bottom_|. + void Extend(int32_t left_offset, + int32_t top_offset, + int32_t right_offset, + int32_t bottom_offset); + private: DesktopRect(int32_t left, int32_t top, int32_t right, int32_t bottom) : left_(left), top_(top), right_(right), bottom_(bottom) { diff --git a/webrtc/modules/desktop_capture/differ_block.cc b/webrtc/modules/desktop_capture/differ_block.cc index d4cbda3601..cf2299f815 100644 --- a/webrtc/modules/desktop_capture/differ_block.cc +++ b/webrtc/modules/desktop_capture/differ_block.cc @@ -13,49 +13,59 @@ #include #include "webrtc/typedefs.h" -#include "webrtc/modules/desktop_capture/differ_block_sse2.h" +#include "webrtc/modules/desktop_capture/differ_vector_sse2.h" #include "webrtc/system_wrappers/include/cpu_features_wrapper.h" namespace webrtc { -bool BlockDifference_C(const uint8_t* image1, - const uint8_t* image2, - int stride) { - int width_bytes = kBlockSize * kBytesPerPixel; +namespace { - for (int y = 0; y < kBlockSize; y++) { - if (memcmp(image1, image2, width_bytes) != 0) +bool VectorDifference_C(const uint8_t* image1, const uint8_t* image2) { + return memcmp(image1, image2, kBlockSize * kBytesPerPixel) != 0; +} + +} // namespace + +bool VectorDifference(const uint8_t* image1, const uint8_t* image2) { + static bool (*diff_proc)(const uint8_t*, const uint8_t*) = nullptr; + + if (!diff_proc) { +#if defined(WEBRTC_ARCH_ARM_FAMILY) || defined(WEBRTC_ARCH_MIPS_FAMILY) + // For ARM and MIPS processors, always use C version. + // TODO(hclam): Implement a NEON version. + diff_proc = &VectorDifference_C; +#else + bool have_sse2 = WebRtc_GetCPUInfo(kSSE2) != 0; + // For x86 processors, check if SSE2 is supported. + if (have_sse2 && kBlockSize == 32) { + diff_proc = &VectorDifference_SSE2_W32; + } else if (have_sse2 && kBlockSize == 16) { + diff_proc = &VectorDifference_SSE2_W16; + } else { + diff_proc = &VectorDifference_C; + } +#endif + } + + return diff_proc(image1, image2); +} + +bool BlockDifference(const uint8_t* image1, + const uint8_t* image2, + int height, + int stride) { + for (int i = 0; i < height; i++) { + if (VectorDifference(image1, image2)) { return true; + } image1 += stride; image2 += stride; } return false; } -bool BlockDifference(const uint8_t* image1, - const uint8_t* image2, - int stride) { - static bool (*diff_proc)(const uint8_t*, const uint8_t*, int) = NULL; - - if (!diff_proc) { -#if defined(WEBRTC_ARCH_ARM_FAMILY) || defined(WEBRTC_ARCH_MIPS_FAMILY) - // For ARM and MIPS processors, always use C version. - // TODO(hclam): Implement a NEON version. - diff_proc = &BlockDifference_C; -#else - bool have_sse2 = WebRtc_GetCPUInfo(kSSE2) != 0; - // For x86 processors, check if SSE2 is supported. - if (have_sse2 && kBlockSize == 32) { - diff_proc = &BlockDifference_SSE2_W32; - } else if (have_sse2 && kBlockSize == 16) { - diff_proc = &BlockDifference_SSE2_W16; - } else { - diff_proc = &BlockDifference_C; - } -#endif - } - - return diff_proc(image1, image2, stride); +bool BlockDifference(const uint8_t* image1, const uint8_t* image2, int stride) { + return BlockDifference(image1, image2, kBlockSize, stride); } } // namespace webrtc diff --git a/webrtc/modules/desktop_capture/differ_block.h b/webrtc/modules/desktop_capture/differ_block.h index e1d487d68b..97fe3bc544 100644 --- a/webrtc/modules/desktop_capture/differ_block.h +++ b/webrtc/modules/desktop_capture/differ_block.h @@ -22,6 +22,17 @@ const int kBlockSize = 32; // Format: BGRA 32 bit. const int kBytesPerPixel = 4; +// Low level function to compare 2 vectors of pixels of size kBlockSize. Returns +// whether the blocks differ. +bool VectorDifference(const uint8_t* image1, const uint8_t* image2); + +// Low level function to compare 2 blocks of pixels of size +// (kBlockSize, |height|). Returns whether the blocks differ. +bool BlockDifference(const uint8_t* image1, + const uint8_t* image2, + int height, + int stride); + // Low level function to compare 2 blocks of pixels of size // (kBlockSize, kBlockSize). Returns whether the blocks differ. bool BlockDifference(const uint8_t* image1, diff --git a/webrtc/modules/desktop_capture/differ_block_sse2.cc b/webrtc/modules/desktop_capture/differ_block_sse2.cc deleted file mode 100644 index 8d35df2226..0000000000 --- a/webrtc/modules/desktop_capture/differ_block_sse2.cc +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2013 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/differ_block_sse2.h" - -#if defined(_MSC_VER) -#include -#else -#include -#include -#endif - -#include "webrtc/modules/desktop_capture/differ_block.h" - -namespace webrtc { - -extern bool BlockDifference_SSE2_W16(const uint8_t* image1, - const uint8_t* image2, - int stride) { - __m128i acc = _mm_setzero_si128(); - __m128i v0; - __m128i v1; - __m128i sad; - for (int y = 0; y < kBlockSize; ++y) { - const __m128i* i1 = reinterpret_cast(image1); - const __m128i* i2 = reinterpret_cast(image2); - v0 = _mm_loadu_si128(i1); - v1 = _mm_loadu_si128(i2); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - v0 = _mm_loadu_si128(i1 + 1); - v1 = _mm_loadu_si128(i2 + 1); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - v0 = _mm_loadu_si128(i1 + 2); - v1 = _mm_loadu_si128(i2 + 2); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - v0 = _mm_loadu_si128(i1 + 3); - v1 = _mm_loadu_si128(i2 + 3); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - - // This essential means sad = acc >> 64. We only care about the lower 16 - // bits. - sad = _mm_shuffle_epi32(acc, 0xEE); - sad = _mm_adds_epu16(sad, acc); - int diff = _mm_cvtsi128_si32(sad); - if (diff) - return true; - image1 += stride; - image2 += stride; - } - return false; -} - -extern bool BlockDifference_SSE2_W32(const uint8_t* image1, - const uint8_t* image2, - int stride) { - __m128i acc = _mm_setzero_si128(); - __m128i v0; - __m128i v1; - __m128i sad; - for (int y = 0; y < kBlockSize; ++y) { - const __m128i* i1 = reinterpret_cast(image1); - const __m128i* i2 = reinterpret_cast(image2); - v0 = _mm_loadu_si128(i1); - v1 = _mm_loadu_si128(i2); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - v0 = _mm_loadu_si128(i1 + 1); - v1 = _mm_loadu_si128(i2 + 1); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - v0 = _mm_loadu_si128(i1 + 2); - v1 = _mm_loadu_si128(i2 + 2); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - v0 = _mm_loadu_si128(i1 + 3); - v1 = _mm_loadu_si128(i2 + 3); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - v0 = _mm_loadu_si128(i1 + 4); - v1 = _mm_loadu_si128(i2 + 4); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - v0 = _mm_loadu_si128(i1 + 5); - v1 = _mm_loadu_si128(i2 + 5); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - v0 = _mm_loadu_si128(i1 + 6); - v1 = _mm_loadu_si128(i2 + 6); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - v0 = _mm_loadu_si128(i1 + 7); - v1 = _mm_loadu_si128(i2 + 7); - sad = _mm_sad_epu8(v0, v1); - acc = _mm_adds_epu16(acc, sad); - - // This essential means sad = acc >> 64. We only care about the lower 16 - // bits. - sad = _mm_shuffle_epi32(acc, 0xEE); - sad = _mm_adds_epu16(sad, acc); - int diff = _mm_cvtsi128_si32(sad); - if (diff) - return true; - image1 += stride; - image2 += stride; - } - return false; -} - -} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/differ_block_sse2.h b/webrtc/modules/desktop_capture/differ_block_sse2.h deleted file mode 100644 index 90426dafab..0000000000 --- a/webrtc/modules/desktop_capture/differ_block_sse2.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2013 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. - */ - -// This header file is used only differ_block.h. It defines the SSE2 rountines -// for finding block difference. - -#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_DIFFER_BLOCK_SSE2_H_ -#define WEBRTC_MODULES_DESKTOP_CAPTURE_DIFFER_BLOCK_SSE2_H_ - -#include - -namespace webrtc { - -// Find block difference of dimension 16x16. -extern bool BlockDifference_SSE2_W16(const uint8_t* image1, - const uint8_t* image2, - int stride); - -// Find block difference of dimension 32x32. -extern bool BlockDifference_SSE2_W32(const uint8_t* image1, - const uint8_t* image2, - int stride); - -} // namespace webrtc - -#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_DIFFER_BLOCK_SSE2_H_ diff --git a/webrtc/modules/desktop_capture/differ_vector_sse2.cc b/webrtc/modules/desktop_capture/differ_vector_sse2.cc new file mode 100644 index 0000000000..ce6cc297c6 --- /dev/null +++ b/webrtc/modules/desktop_capture/differ_vector_sse2.cc @@ -0,0 +1,102 @@ +/* + * 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/differ_vector_sse2.h" + +#if defined(_MSC_VER) +#include +#else +#include +#include +#endif + +namespace webrtc { + +extern bool VectorDifference_SSE2_W16(const uint8_t* image1, + const uint8_t* image2) { + __m128i acc = _mm_setzero_si128(); + __m128i v0; + __m128i v1; + __m128i sad; + const __m128i* i1 = reinterpret_cast(image1); + const __m128i* i2 = reinterpret_cast(image2); + v0 = _mm_loadu_si128(i1); + v1 = _mm_loadu_si128(i2); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + v0 = _mm_loadu_si128(i1 + 1); + v1 = _mm_loadu_si128(i2 + 1); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + v0 = _mm_loadu_si128(i1 + 2); + v1 = _mm_loadu_si128(i2 + 2); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + v0 = _mm_loadu_si128(i1 + 3); + v1 = _mm_loadu_si128(i2 + 3); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + + // This essential means sad = acc >> 64. We only care about the lower 16 + // bits. + sad = _mm_shuffle_epi32(acc, 0xEE); + sad = _mm_adds_epu16(sad, acc); + return _mm_cvtsi128_si32(sad) != 0; +} + +extern bool VectorDifference_SSE2_W32(const uint8_t* image1, + const uint8_t* image2) { + __m128i acc = _mm_setzero_si128(); + __m128i v0; + __m128i v1; + __m128i sad; + const __m128i* i1 = reinterpret_cast(image1); + const __m128i* i2 = reinterpret_cast(image2); + v0 = _mm_loadu_si128(i1); + v1 = _mm_loadu_si128(i2); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + v0 = _mm_loadu_si128(i1 + 1); + v1 = _mm_loadu_si128(i2 + 1); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + v0 = _mm_loadu_si128(i1 + 2); + v1 = _mm_loadu_si128(i2 + 2); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + v0 = _mm_loadu_si128(i1 + 3); + v1 = _mm_loadu_si128(i2 + 3); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + v0 = _mm_loadu_si128(i1 + 4); + v1 = _mm_loadu_si128(i2 + 4); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + v0 = _mm_loadu_si128(i1 + 5); + v1 = _mm_loadu_si128(i2 + 5); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + v0 = _mm_loadu_si128(i1 + 6); + v1 = _mm_loadu_si128(i2 + 6); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + v0 = _mm_loadu_si128(i1 + 7); + v1 = _mm_loadu_si128(i2 + 7); + sad = _mm_sad_epu8(v0, v1); + acc = _mm_adds_epu16(acc, sad); + + // This essential means sad = acc >> 64. We only care about the lower 16 + // bits. + sad = _mm_shuffle_epi32(acc, 0xEE); + sad = _mm_adds_epu16(sad, acc); + return _mm_cvtsi128_si32(sad) != 0; +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/differ_vector_sse2.h b/webrtc/modules/desktop_capture/differ_vector_sse2.h new file mode 100644 index 0000000000..353c6b9b00 --- /dev/null +++ b/webrtc/modules/desktop_capture/differ_vector_sse2.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +// This header file is used only differ_block.h. It defines the SSE2 rountines +// for finding vector difference. + +#ifndef WEBRTC_MODULES_DESKTOP_CAPTURE_DIFFER_VECTOR_SSE2_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_DIFFER_VECTOR_SSE2_H_ + +#include + +namespace webrtc { + +// Find vector difference of dimension 16. +extern bool VectorDifference_SSE2_W16(const uint8_t* image1, + const uint8_t* image2); + +// Find vector difference of dimension 32. +extern bool VectorDifference_SSE2_W32(const uint8_t* image1, + const uint8_t* image2); + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_DIFFER_VECTOR_SSE2_H_ diff --git a/webrtc/modules/desktop_capture/fake_desktop_capturer.h b/webrtc/modules/desktop_capture/fake_desktop_capturer.h new file mode 100644 index 0000000000..db53809ec5 --- /dev/null +++ b/webrtc/modules/desktop_capture/fake_desktop_capturer.h @@ -0,0 +1,95 @@ +/* + * 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_FAKE_DESKTOP_CAPTURER_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_FAKE_DESKTOP_CAPTURER_H_ + +#include +#include + +#include "webrtc/modules/desktop_capture/desktop_capturer.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" + +namespace webrtc { + +// A fake implementation of DesktopCapturer or its derived interfaces to +// generate DesktopFrame for testing purpose. +// +// Consumers can provide a FrameGenerator instance to generate instances of +// DesktopFrame to return for each Capture() function call. +// If no FrameGenerator provided, FakeDesktopCapturer will always return a +// nullptr DesktopFrame. +// +// Double buffering is guaranteed by the FrameGenerator. FrameGenerator +// implements in desktop_frame_generator.h guarantee double buffering, they +// creates a new instance of DesktopFrame each time. +// +// T must be DesktopCapturer or its derived interfaces. +// +// TODO(zijiehe): Remove template T once we merge ScreenCapturer and +// WindowCapturer. +template +class FakeDesktopCapturer : public T { + public: + FakeDesktopCapturer() + : callback_(nullptr), + result_(DesktopCapturer::Result::SUCCESS), + generator_(nullptr) {} + + ~FakeDesktopCapturer() override {} + + // Decides the result which will be returned in next Capture() callback. + void set_result(DesktopCapturer::Result result) { result_ = result; } + + // Uses the |generator| provided as DesktopFrameGenerator, FakeDesktopCapturer + // does + // not take the ownership of |generator|. + void set_frame_generator(DesktopFrameGenerator* generator) { + generator_ = generator; + } + + // DesktopCapturer interface + void Start(DesktopCapturer::Callback* callback) override { + callback_ = callback; + } + + void Capture(const DesktopRegion& region) override { + if (generator_) { + std::unique_ptr frame( + generator_->GetNextFrame(shared_memory_factory_.get())); + if (frame) { + callback_->OnCaptureResult(result_, std::move(frame)); + } else { + callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY, + nullptr); + } + return; + } + callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, + nullptr); + } + + void SetSharedMemoryFactory( + std::unique_ptr shared_memory_factory) override { + shared_memory_factory_ = std::move(shared_memory_factory); + } + + private: + DesktopCapturer::Callback* callback_; + std::unique_ptr shared_memory_factory_; + DesktopCapturer::Result result_; + DesktopFrameGenerator* generator_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_FAKE_DESKTOP_CAPTURER_H_ diff --git a/webrtc/modules/desktop_capture/fake_screen_capturer.cc b/webrtc/modules/desktop_capture/fake_screen_capturer.cc new file mode 100644 index 0000000000..192992fcff --- /dev/null +++ b/webrtc/modules/desktop_capture/fake_screen_capturer.cc @@ -0,0 +1,34 @@ +/* + * 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/fake_screen_capturer.h" + +#include + +#include + +namespace webrtc { + +FakeScreenCapturer::FakeScreenCapturer() + : FakeDesktopCapturer(), + // A random number for the fake screen. + id_(1378277498) {} +FakeScreenCapturer::~FakeScreenCapturer() {} + +bool FakeScreenCapturer::GetScreenList(ScreenList* list) { + list->push_back(Screen{id_}); + return true; +} + +bool FakeScreenCapturer::SelectScreen(ScreenId id) { + return id == kFullDesktopScreenId || id == id_; +} + +} // namespace webrtc diff --git a/webrtc/modules/desktop_capture/fake_screen_capturer.h b/webrtc/modules/desktop_capture/fake_screen_capturer.h new file mode 100644 index 0000000000..3b7c6fc25f --- /dev/null +++ b/webrtc/modules/desktop_capture/fake_screen_capturer.h @@ -0,0 +1,35 @@ +/* + * 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_FAKE_SCREEN_CAPTURER_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_FAKE_SCREEN_CAPTURER_H_ + +#include "webrtc/modules/desktop_capture/fake_desktop_capturer.h" +#include "webrtc/modules/desktop_capture/screen_capturer.h" + +namespace webrtc { + +class FakeScreenCapturer : public FakeDesktopCapturer { + public: + FakeScreenCapturer(); + ~FakeScreenCapturer() override; + + // ScreenCapturer interface. + bool GetScreenList(ScreenList* list) override; + bool SelectScreen(ScreenId id) override; + + private: + // A random ScreenId. + const ScreenId id_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_FAKE_SCREEN_CAPTURER_H_ diff --git a/webrtc/modules/desktop_capture/screen_capturer_differ_wrapper.cc b/webrtc/modules/desktop_capture/screen_capturer_differ_wrapper.cc new file mode 100644 index 0000000000..dc76b67353 --- /dev/null +++ b/webrtc/modules/desktop_capture/screen_capturer_differ_wrapper.cc @@ -0,0 +1,214 @@ +/* + * 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/screen_capturer_differ_wrapper.h" + +#include +#include + +#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 + +ScreenCapturerDifferWrapper::ScreenCapturerDifferWrapper( + std::unique_ptr base_capturer) + : base_capturer_(std::move(base_capturer)) { + RTC_DCHECK(base_capturer_); +} + +ScreenCapturerDifferWrapper::~ScreenCapturerDifferWrapper() {} + +void ScreenCapturerDifferWrapper::Start(DesktopCapturer::Callback* callback) { + callback_ = callback; + base_capturer_->Start(this); +} + +void ScreenCapturerDifferWrapper::SetSharedMemoryFactory( + std::unique_ptr shared_memory_factory) { + base_capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory)); +} + +void ScreenCapturerDifferWrapper::Capture(const DesktopRegion& region) { + base_capturer_->Capture(region); +} + +bool ScreenCapturerDifferWrapper::GetScreenList(ScreenList* screens) { + return base_capturer_->GetScreenList(screens); +} + +bool ScreenCapturerDifferWrapper::SelectScreen(ScreenId id) { + return base_capturer_->SelectScreen(id); +} + +void ScreenCapturerDifferWrapper::OnCaptureResult( + Result result, + std::unique_ptr 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 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 diff --git a/webrtc/modules/desktop_capture/screen_capturer_differ_wrapper.h b/webrtc/modules/desktop_capture/screen_capturer_differ_wrapper.h new file mode 100644 index 0000000000..c7a20b3d07 --- /dev/null +++ b/webrtc/modules/desktop_capture/screen_capturer_differ_wrapper.h @@ -0,0 +1,58 @@ +/* + * 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_SCREEN_CAPTURER_DIFFER_WRAPPER_H_ +#define WEBRTC_MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_DIFFER_WRAPPER_H_ + +#include + +#include "webrtc/modules/desktop_capture/screen_capturer.h" +#include "webrtc/modules/desktop_capture/shared_desktop_frame.h" + +namespace webrtc { + +// 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 +// ScreenCapturer 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 ScreenCapturerDifferWrapper : public ScreenCapturer, + public DesktopCapturer::Callback { + public: + // Creates a ScreenCapturerDifferWrapper with a ScreenCapturer implementation, + // and takes its ownership. + explicit ScreenCapturerDifferWrapper( + std::unique_ptr base_capturer); + ~ScreenCapturerDifferWrapper() override; + + // ScreenCapturer interface. + void Start(DesktopCapturer::Callback* callback) override; + void SetSharedMemoryFactory( + std::unique_ptr shared_memory_factory) override; + void Capture(const DesktopRegion& region) override; + bool GetScreenList(ScreenList* screens) override; + bool SelectScreen(ScreenId id) override; + + private: + // DesktopCapturer::Callback interface. + void OnCaptureResult(Result result, + std::unique_ptr frame) override; + + const std::unique_ptr base_capturer_; + DesktopCapturer::Callback* callback_; + std::unique_ptr last_frame_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_DIFFER_WRAPPER_H_ diff --git a/webrtc/modules/desktop_capture/screen_capturer_differ_wrapper_unittest.cc b/webrtc/modules/desktop_capture/screen_capturer_differ_wrapper_unittest.cc new file mode 100644 index 0000000000..e667553b14 --- /dev/null +++ b/webrtc/modules/desktop_capture/screen_capturer_differ_wrapper_unittest.cc @@ -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/screen_capturer_differ_wrapper.h" + +#include +#include +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#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/screen_capturer_mock_objects.h" +#include "webrtc/system_wrappers/include/cpu_features_wrapper.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