summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/desktop_capture/win
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/modules/desktop_capture/win')
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor.cc233
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor.h25
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.curbin0 -> 3262 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.curbin0 -> 4286 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.curbin0 -> 2238 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.curbin0 -> 326 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.curbin0 -> 4286 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.curbin0 -> 4286 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.curbin0 -> 766 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest.cc91
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.h24
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.rc28
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/d3d_device.cc100
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/d3d_device.h59
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/desktop.cc111
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/desktop.h65
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.cc32
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.h29
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.cc66
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.h53
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc185
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h92
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.cc33
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.h62
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc515
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h257
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.cc77
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.h63
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc423
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.h154
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.cc81
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.h73
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc58
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.h47
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.cc132
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.h68
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc294
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.h25
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/scoped_gdi_object.h91
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.cc54
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.h55
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.cc206
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.h79
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils_unittest.cc81
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc246
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.h108
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx_unittest.cc41
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc240
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h83
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc390
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h140
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.cc59
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.h45
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.cc104
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.h51
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.cc591
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.h154
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.cc227
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.h146
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source_unittest.cc148
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc365
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h169
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win_unittest.cc572
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.cc25
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.h46
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.cc486
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.h136
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils_unittest.cc153
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.cc403
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.h78
70 files changed, 9327 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor.cc b/third_party/libwebrtc/modules/desktop_capture/win/cursor.cc
new file mode 100644
index 0000000000..1d645098e2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor.cc
@@ -0,0 +1,233 @@
+/*
+ * 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 "modules/desktop_capture/win/cursor.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/win/scoped_gdi_object.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/system/arch.h"
+
+namespace webrtc {
+
+namespace {
+
+#if defined(WEBRTC_ARCH_LITTLE_ENDIAN)
+
+#define RGBA(r, g, b, a) \
+ ((((a) << 24) & 0xff000000) | (((b) << 16) & 0xff0000) | \
+ (((g) << 8) & 0xff00) | ((r)&0xff))
+
+#else // !defined(WEBRTC_ARCH_LITTLE_ENDIAN)
+
+#define RGBA(r, g, b, a) \
+ ((((r) << 24) & 0xff000000) | (((g) << 16) & 0xff0000) | \
+ (((b) << 8) & 0xff00) | ((a)&0xff))
+
+#endif // !defined(WEBRTC_ARCH_LITTLE_ENDIAN)
+
+const int kBytesPerPixel = DesktopFrame::kBytesPerPixel;
+
+// Pixel colors used when generating cursor outlines.
+const uint32_t kPixelRgbaBlack = RGBA(0, 0, 0, 0xff);
+const uint32_t kPixelRgbaWhite = RGBA(0xff, 0xff, 0xff, 0xff);
+const uint32_t kPixelRgbaTransparent = RGBA(0, 0, 0, 0);
+
+const uint32_t kPixelRgbWhite = RGB(0xff, 0xff, 0xff);
+
+// Expands the cursor shape to add a white outline for visibility against
+// dark backgrounds.
+void AddCursorOutline(int width, int height, uint32_t* data) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ // If this is a transparent pixel (bgr == 0 and alpha = 0), check the
+ // neighbor pixels to see if this should be changed to an outline pixel.
+ if (*data == kPixelRgbaTransparent) {
+ // Change to white pixel if any neighbors (top, bottom, left, right)
+ // are black.
+ if ((y > 0 && data[-width] == kPixelRgbaBlack) ||
+ (y < height - 1 && data[width] == kPixelRgbaBlack) ||
+ (x > 0 && data[-1] == kPixelRgbaBlack) ||
+ (x < width - 1 && data[1] == kPixelRgbaBlack)) {
+ *data = kPixelRgbaWhite;
+ }
+ }
+ data++;
+ }
+ }
+}
+
+// Premultiplies RGB components of the pixel data in the given image by
+// the corresponding alpha components.
+void AlphaMul(uint32_t* data, int width, int height) {
+ static_assert(sizeof(uint32_t) == kBytesPerPixel,
+ "size of uint32 should be the number of bytes per pixel");
+
+ for (uint32_t* data_end = data + width * height; data != data_end; ++data) {
+ RGBQUAD* from = reinterpret_cast<RGBQUAD*>(data);
+ RGBQUAD* to = reinterpret_cast<RGBQUAD*>(data);
+ to->rgbBlue =
+ (static_cast<uint16_t>(from->rgbBlue) * from->rgbReserved) / 0xff;
+ to->rgbGreen =
+ (static_cast<uint16_t>(from->rgbGreen) * from->rgbReserved) / 0xff;
+ to->rgbRed =
+ (static_cast<uint16_t>(from->rgbRed) * from->rgbReserved) / 0xff;
+ }
+}
+
+// Scans a 32bpp bitmap looking for any pixels with non-zero alpha component.
+// Returns true if non-zero alpha is found. `stride` is expressed in pixels.
+bool HasAlphaChannel(const uint32_t* data, int stride, int width, int height) {
+ const RGBQUAD* plane = reinterpret_cast<const RGBQUAD*>(data);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ if (plane->rgbReserved != 0)
+ return true;
+ plane += 1;
+ }
+ plane += stride - width;
+ }
+
+ return false;
+}
+
+} // namespace
+
+MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor) {
+ ICONINFO iinfo;
+ if (!GetIconInfo(cursor, &iinfo)) {
+ RTC_LOG_F(LS_ERROR) << "Unable to get cursor icon info. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ int hotspot_x = iinfo.xHotspot;
+ int hotspot_y = iinfo.yHotspot;
+
+ // Make sure the bitmaps will be freed.
+ win::ScopedBitmap scoped_mask(iinfo.hbmMask);
+ win::ScopedBitmap scoped_color(iinfo.hbmColor);
+ bool is_color = iinfo.hbmColor != NULL;
+
+ // Get `scoped_mask` dimensions.
+ BITMAP bitmap_info;
+ if (!GetObject(scoped_mask, sizeof(bitmap_info), &bitmap_info)) {
+ RTC_LOG_F(LS_ERROR) << "Unable to get bitmap info. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ int width = bitmap_info.bmWidth;
+ int height = bitmap_info.bmHeight;
+ std::unique_ptr<uint32_t[]> mask_data(new uint32_t[width * height]);
+
+ // Get pixel data from `scoped_mask` converting it to 32bpp along the way.
+ // GetDIBits() sets the alpha component of every pixel to 0.
+ BITMAPV5HEADER bmi = {0};
+ bmi.bV5Size = sizeof(bmi);
+ bmi.bV5Width = width;
+ bmi.bV5Height = -height; // request a top-down bitmap.
+ bmi.bV5Planes = 1;
+ bmi.bV5BitCount = kBytesPerPixel * 8;
+ bmi.bV5Compression = BI_RGB;
+ bmi.bV5AlphaMask = 0xff000000;
+ bmi.bV5CSType = LCS_WINDOWS_COLOR_SPACE;
+ bmi.bV5Intent = LCS_GM_BUSINESS;
+ if (!GetDIBits(dc, scoped_mask, 0, height, mask_data.get(),
+ reinterpret_cast<BITMAPINFO*>(&bmi), DIB_RGB_COLORS)) {
+ RTC_LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ uint32_t* mask_plane = mask_data.get();
+ std::unique_ptr<DesktopFrame> image(
+ new BasicDesktopFrame(DesktopSize(width, height)));
+ bool has_alpha = false;
+
+ if (is_color) {
+ image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
+ // Get the pixels from the color bitmap.
+ if (!GetDIBits(dc, scoped_color, 0, height, image->data(),
+ reinterpret_cast<BITMAPINFO*>(&bmi), DIB_RGB_COLORS)) {
+ RTC_LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ // GetDIBits() does not provide any indication whether the bitmap has alpha
+ // channel, so we use HasAlphaChannel() below to find it out.
+ has_alpha = HasAlphaChannel(reinterpret_cast<uint32_t*>(image->data()),
+ width, width, height);
+ } else {
+ // For non-color cursors, the mask contains both an AND and an XOR mask and
+ // the height includes both. Thus, the width is correct, but we need to
+ // divide by 2 to get the correct mask height.
+ height /= 2;
+
+ image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
+
+ // The XOR mask becomes the color bitmap.
+ memcpy(image->data(), mask_plane + (width * height),
+ image->stride() * height);
+ }
+
+ // Reconstruct transparency from the mask if the color image does not has
+ // alpha channel.
+ if (!has_alpha) {
+ bool add_outline = false;
+ uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
+ uint32_t* mask = mask_plane;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ // The two bitmaps combine as follows:
+ // mask color Windows Result Our result RGB Alpha
+ // 0 00 Black Black 00 ff
+ // 0 ff White White ff ff
+ // 1 00 Screen Transparent 00 00
+ // 1 ff Reverse-screen Black 00 ff
+ //
+ // Since we don't support XOR cursors, we replace the "Reverse Screen"
+ // with black. In this case, we also add an outline around the cursor
+ // so that it is visible against a dark background.
+ if (*mask == kPixelRgbWhite) {
+ if (*dst != 0) {
+ add_outline = true;
+ *dst = kPixelRgbaBlack;
+ } else {
+ *dst = kPixelRgbaTransparent;
+ }
+ } else {
+ *dst = kPixelRgbaBlack ^ *dst;
+ }
+
+ ++dst;
+ ++mask;
+ }
+ }
+ if (add_outline) {
+ AddCursorOutline(width, height,
+ reinterpret_cast<uint32_t*>(image->data()));
+ }
+ }
+
+ // Pre-multiply the resulting pixels since MouseCursor uses premultiplied
+ // images.
+ AlphaMul(reinterpret_cast<uint32_t*>(image->data()), width, height);
+
+ return new MouseCursor(image.release(), DesktopVector(hotspot_x, hotspot_y));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor.h b/third_party/libwebrtc/modules/desktop_capture/win/cursor.h
new file mode 100644
index 0000000000..54d78164a3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor.h
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_
+
+#include <windows.h>
+
+namespace webrtc {
+
+class MouseCursor;
+
+// Converts an HCURSOR into a `MouseCursor` instance.
+MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.cur
new file mode 100644
index 0000000000..27702b825c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.cur
new file mode 100644
index 0000000000..7e0d8596da
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.cur
new file mode 100644
index 0000000000..fefb09e1a1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.cur
new file mode 100644
index 0000000000..4f8a094f31
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.cur
new file mode 100644
index 0000000000..ac9cdbfbb3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.cur
new file mode 100644
index 0000000000..efdbee5415
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.cur
new file mode 100644
index 0000000000..9678d55446
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest.cc
new file mode 100644
index 0000000000..23f5d89571
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest.cc
@@ -0,0 +1,91 @@
+/*
+ * 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 "modules/desktop_capture/win/cursor.h"
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/win/cursor_unittest_resources.h"
+#include "modules/desktop_capture/win/scoped_gdi_object.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+namespace {
+
+// Loads `left` from resources, converts it to a `MouseCursor` instance and
+// compares pixels with `right`. Returns true of MouseCursor bits match `right`.
+// `right` must be a 32bpp cursor with alpha channel.
+bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
+ HMODULE instance = GetModuleHandle(NULL);
+
+ // Load `left` from the EXE module's resources.
+ win::ScopedCursor cursor(reinterpret_cast<HCURSOR>(
+ LoadImage(instance, MAKEINTRESOURCE(left), IMAGE_CURSOR, 0, 0, 0)));
+ EXPECT_TRUE(cursor != NULL);
+
+ // Convert `cursor` to `mouse_shape`.
+ HDC dc = GetDC(NULL);
+ std::unique_ptr<MouseCursor> mouse_shape(
+ CreateMouseCursorFromHCursor(dc, cursor));
+ ReleaseDC(NULL, dc);
+
+ EXPECT_TRUE(mouse_shape.get());
+
+ // Load `right`.
+ cursor.Set(reinterpret_cast<HCURSOR>(
+ LoadImage(instance, MAKEINTRESOURCE(right), IMAGE_CURSOR, 0, 0, 0)));
+
+ ICONINFO iinfo;
+ EXPECT_TRUE(GetIconInfo(cursor, &iinfo));
+ EXPECT_TRUE(iinfo.hbmColor);
+
+ // Make sure the bitmaps will be freed.
+ win::ScopedBitmap scoped_mask(iinfo.hbmMask);
+ win::ScopedBitmap scoped_color(iinfo.hbmColor);
+
+ // Get `scoped_color` dimensions.
+ BITMAP bitmap_info;
+ EXPECT_TRUE(GetObject(scoped_color, sizeof(bitmap_info), &bitmap_info));
+
+ int width = bitmap_info.bmWidth;
+ int height = bitmap_info.bmHeight;
+ EXPECT_TRUE(DesktopSize(width, height).equals(mouse_shape->image()->size()));
+
+ // Get the pixels from `scoped_color`.
+ int size = width * height;
+ std::unique_ptr<uint32_t[]> data(new uint32_t[size]);
+ EXPECT_TRUE(GetBitmapBits(scoped_color, size * sizeof(uint32_t), data.get()));
+
+ // Compare the 32bpp image in `mouse_shape` with the one loaded from `right`.
+ return memcmp(data.get(), mouse_shape->image()->data(),
+ size * sizeof(uint32_t)) == 0;
+}
+
+} // namespace
+
+TEST(MouseCursorTest, MatchCursors) {
+ EXPECT_TRUE(
+ ConvertToMouseShapeAndCompare(IDD_CURSOR1_24BPP, IDD_CURSOR1_32BPP));
+
+ EXPECT_TRUE(
+ ConvertToMouseShapeAndCompare(IDD_CURSOR1_8BPP, IDD_CURSOR1_32BPP));
+
+ EXPECT_TRUE(
+ ConvertToMouseShapeAndCompare(IDD_CURSOR2_1BPP, IDD_CURSOR2_32BPP));
+
+ EXPECT_TRUE(
+ ConvertToMouseShapeAndCompare(IDD_CURSOR3_4BPP, IDD_CURSOR3_32BPP));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.h b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.h
new file mode 100644
index 0000000000..f583554d68
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_
+
+#define IDD_CURSOR1_24BPP 101
+#define IDD_CURSOR1_32BPP 102
+#define IDD_CURSOR1_8BPP 103
+
+#define IDD_CURSOR2_1BPP 104
+#define IDD_CURSOR2_32BPP 105
+
+#define IDD_CURSOR3_4BPP 106
+#define IDD_CURSOR3_32BPP 107
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.rc b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.rc
new file mode 100644
index 0000000000..90073791c9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.rc
@@ -0,0 +1,28 @@
+/*
+ * 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 "modules/desktop_capture/win/cursor_unittest_resources.h"
+
+// These cursors are matched with their less than 32bpp counterparts below.
+IDD_CURSOR1_32BPP CURSOR "cursor_test_data/1_32bpp.cur"
+IDD_CURSOR2_32BPP CURSOR "cursor_test_data/2_32bpp.cur"
+IDD_CURSOR3_32BPP CURSOR "cursor_test_data/3_32bpp.cur"
+
+// Matches IDD_CURSOR1_32BPP.
+IDD_CURSOR1_24BPP CURSOR "cursor_test_data/1_24bpp.cur"
+
+// Matches IDD_CURSOR1_32BPP.
+IDD_CURSOR1_8BPP CURSOR "cursor_test_data/1_8bpp.cur"
+
+// Matches IDD_CURSOR2_32BPP.
+IDD_CURSOR2_1BPP CURSOR "cursor_test_data/2_1bpp.cur"
+
+// Matches IDD_CURSOR3_32BPP.
+IDD_CURSOR3_4BPP CURSOR "cursor_test_data/3_4bpp.cur"
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.cc b/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.cc
new file mode 100644
index 0000000000..3d46117501
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.cc
@@ -0,0 +1,100 @@
+/*
+ * 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 "modules/desktop_capture/win/d3d_device.h"
+
+#include <utility>
+
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+using Microsoft::WRL::ComPtr;
+
+D3dDevice::D3dDevice() = default;
+D3dDevice::D3dDevice(const D3dDevice& other) = default;
+D3dDevice::D3dDevice(D3dDevice&& other) = default;
+D3dDevice::~D3dDevice() = default;
+
+bool D3dDevice::Initialize(const ComPtr<IDXGIAdapter>& adapter) {
+ dxgi_adapter_ = adapter;
+ if (!dxgi_adapter_) {
+ RTC_LOG(LS_WARNING) << "An empty IDXGIAdapter instance has been received.";
+ return false;
+ }
+
+ D3D_FEATURE_LEVEL feature_level;
+ // Default feature levels contain D3D 9.1 through D3D 11.0.
+ _com_error error = D3D11CreateDevice(
+ adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr,
+ D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_SINGLETHREADED,
+ nullptr, 0, D3D11_SDK_VERSION, d3d_device_.GetAddressOf(), &feature_level,
+ context_.GetAddressOf());
+ if (error.Error() != S_OK || !d3d_device_ || !context_) {
+ RTC_LOG(LS_WARNING) << "D3D11CreateDevice returned: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ if (feature_level < D3D_FEATURE_LEVEL_11_0) {
+ RTC_LOG(LS_WARNING)
+ << "D3D11CreateDevice returned an instance without DirectX 11 support, "
+ << "level " << feature_level << ". Following initialization may fail.";
+ // D3D_FEATURE_LEVEL_11_0 is not officially documented on MSDN to be a
+ // requirement of Dxgi duplicator APIs.
+ }
+
+ error = d3d_device_.As(&dxgi_device_);
+ if (error.Error() != S_OK || !dxgi_device_) {
+ RTC_LOG(LS_WARNING)
+ << "ID3D11Device is not an implementation of IDXGIDevice, "
+ << "this usually means the system does not support DirectX "
+ << "11. Error received: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ return true;
+}
+
+// static
+std::vector<D3dDevice> D3dDevice::EnumDevices() {
+ ComPtr<IDXGIFactory1> factory;
+ _com_error error =
+ CreateDXGIFactory1(__uuidof(IDXGIFactory1),
+ reinterpret_cast<void**>(factory.GetAddressOf()));
+ if (error.Error() != S_OK || !factory) {
+ RTC_LOG(LS_WARNING) << "Cannot create IDXGIFactory1: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return std::vector<D3dDevice>();
+ }
+
+ std::vector<D3dDevice> result;
+ for (int i = 0;; i++) {
+ ComPtr<IDXGIAdapter> adapter;
+ error = factory->EnumAdapters(i, adapter.GetAddressOf());
+ if (error.Error() == S_OK) {
+ D3dDevice device;
+ if (device.Initialize(adapter)) {
+ result.push_back(std::move(device));
+ }
+ } else if (error.Error() == DXGI_ERROR_NOT_FOUND) {
+ break;
+ } else {
+ RTC_LOG(LS_WARNING)
+ << "IDXGIFactory1::EnumAdapters returned an unexpected error: "
+ << desktop_capture::utils::ComErrorToString(error);
+ }
+ }
+ return result;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.h b/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.h
new file mode 100644
index 0000000000..aeb9d1823a
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.h
@@ -0,0 +1,59 @@
+/*
+ * 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 MODULES_DESKTOP_CAPTURE_WIN_D3D_DEVICE_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_D3D_DEVICE_H_
+
+#include <comdef.h>
+#include <d3d11.h>
+#include <dxgi.h>
+#include <wrl/client.h>
+
+#include <vector>
+
+namespace webrtc {
+
+// A wrapper of ID3D11Device and its corresponding context and IDXGIAdapter.
+// This class represents one video card in the system.
+class D3dDevice {
+ public:
+ D3dDevice(const D3dDevice& other);
+ D3dDevice(D3dDevice&& other);
+ ~D3dDevice();
+
+ ID3D11Device* d3d_device() const { return d3d_device_.Get(); }
+
+ ID3D11DeviceContext* context() const { return context_.Get(); }
+
+ IDXGIDevice* dxgi_device() const { return dxgi_device_.Get(); }
+
+ IDXGIAdapter* dxgi_adapter() const { return dxgi_adapter_.Get(); }
+
+ // Returns all D3dDevice instances on the system. Returns an empty vector if
+ // anything wrong.
+ static std::vector<D3dDevice> EnumDevices();
+
+ private:
+ // Instances of D3dDevice should only be created by EnumDevices() static
+ // function.
+ D3dDevice();
+
+ // Initializes the D3dDevice from an IDXGIAdapter.
+ bool Initialize(const Microsoft::WRL::ComPtr<IDXGIAdapter>& adapter);
+
+ Microsoft::WRL::ComPtr<ID3D11Device> d3d_device_;
+ Microsoft::WRL::ComPtr<ID3D11DeviceContext> context_;
+ Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device_;
+ Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_D3D_DEVICE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/desktop.cc b/third_party/libwebrtc/modules/desktop_capture/win/desktop.cc
new file mode 100644
index 0000000000..4a671dd9ae
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/desktop.cc
@@ -0,0 +1,111 @@
+/*
+ * 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 "modules/desktop_capture/win/desktop.h"
+
+#include <vector>
+
+#include "rtc_base/logging.h"
+#include "rtc_base/string_utils.h"
+
+namespace webrtc {
+
+Desktop::Desktop(HDESK desktop, bool own) : desktop_(desktop), own_(own) {}
+
+Desktop::~Desktop() {
+ if (own_ && desktop_ != NULL) {
+ if (!::CloseDesktop(desktop_)) {
+ RTC_LOG(LS_ERROR) << "Failed to close the owned desktop handle: "
+ << GetLastError();
+ }
+ }
+}
+
+bool Desktop::GetName(std::wstring* desktop_name_out) const {
+ if (desktop_ == NULL)
+ return false;
+
+ DWORD length = 0;
+ int rv = GetUserObjectInformationW(desktop_, UOI_NAME, NULL, 0, &length);
+ if (rv || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ abort();
+
+ length /= sizeof(WCHAR);
+ std::vector<WCHAR> buffer(length);
+ if (!GetUserObjectInformationW(desktop_, UOI_NAME, &buffer[0],
+ length * sizeof(WCHAR), &length)) {
+ RTC_LOG(LS_ERROR) << "Failed to query the desktop name: " << GetLastError();
+ return false;
+ }
+
+ desktop_name_out->assign(&buffer[0], length / sizeof(WCHAR));
+ return true;
+}
+
+bool Desktop::IsSame(const Desktop& other) const {
+ std::wstring name;
+ if (!GetName(&name))
+ return false;
+
+ std::wstring other_name;
+ if (!other.GetName(&other_name))
+ return false;
+
+ return name == other_name;
+}
+
+bool Desktop::SetThreadDesktop() const {
+ if (!::SetThreadDesktop(desktop_)) {
+ RTC_LOG(LS_ERROR) << "Failed to assign the desktop to the current thread: "
+ << GetLastError();
+ return false;
+ }
+
+ return true;
+}
+
+Desktop* Desktop::GetDesktop(const WCHAR* desktop_name) {
+ ACCESS_MASK desired_access = DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+ DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+ DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+ DESKTOP_SWITCHDESKTOP | GENERIC_WRITE;
+ HDESK desktop = OpenDesktopW(desktop_name, 0, FALSE, desired_access);
+ if (desktop == NULL) {
+ RTC_LOG(LS_ERROR) << "Failed to open the desktop '"
+ << rtc::ToUtf8(desktop_name) << "': " << GetLastError();
+ return NULL;
+ }
+
+ return new Desktop(desktop, true);
+}
+
+Desktop* Desktop::GetInputDesktop() {
+ HDESK desktop = OpenInputDesktop(
+ 0, FALSE, GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
+ if (desktop == NULL)
+ return NULL;
+
+ return new Desktop(desktop, true);
+}
+
+Desktop* Desktop::GetThreadDesktop() {
+ HDESK desktop = ::GetThreadDesktop(GetCurrentThreadId());
+ if (desktop == NULL) {
+ RTC_LOG(LS_ERROR)
+ << "Failed to retrieve the handle of the desktop assigned to "
+ "the current thread: "
+ << GetLastError();
+ return NULL;
+ }
+
+ return new Desktop(desktop, false);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/desktop.h b/third_party/libwebrtc/modules/desktop_capture/win/desktop.h
new file mode 100644
index 0000000000..01bed8592d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/desktop.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_H_
+
+#include <windows.h>
+
+#include <string>
+
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+class RTC_EXPORT Desktop {
+ public:
+ ~Desktop();
+
+ Desktop(const Desktop&) = delete;
+ Desktop& operator=(const Desktop&) = delete;
+
+ // Returns the name of the desktop represented by the object. Return false if
+ // quering the name failed for any reason.
+ bool GetName(std::wstring* desktop_name_out) const;
+
+ // Returns true if `other` has the same name as this desktop. Returns false
+ // in any other case including failing Win32 APIs and uninitialized desktop
+ // handles.
+ bool IsSame(const Desktop& other) const;
+
+ // Assigns the desktop to the current thread. Returns false is the operation
+ // failed for any reason.
+ bool SetThreadDesktop() const;
+
+ // Returns the desktop by its name or NULL if an error occurs.
+ static Desktop* GetDesktop(const wchar_t* desktop_name);
+
+ // Returns the desktop currently receiving user input or NULL if an error
+ // occurs.
+ static Desktop* GetInputDesktop();
+
+ // Returns the desktop currently assigned to the calling thread or NULL if
+ // an error occurs.
+ static Desktop* GetThreadDesktop();
+
+ private:
+ Desktop(HDESK desktop, bool own);
+
+ // The desktop handle.
+ HDESK desktop_;
+
+ // True if `desktop_` must be closed on teardown.
+ bool own_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.cc b/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.cc
new file mode 100644
index 0000000000..476ddc4aba
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 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 "modules/desktop_capture/win/desktop_capture_utils.h"
+
+#include "rtc_base/strings/string_builder.h"
+
+namespace webrtc {
+namespace desktop_capture {
+namespace utils {
+
+// Generates a human-readable string from a COM error.
+std::string ComErrorToString(const _com_error& error) {
+ char buffer[1024];
+ rtc::SimpleStringBuilder string_builder(buffer);
+ // Use _bstr_t to simplify the wchar to char conversion for ErrorMessage().
+ _bstr_t error_message(error.ErrorMessage());
+ string_builder.AppendFormat("HRESULT: 0x%08X, Message: %s", error.Error(),
+ static_cast<const char*>(error_message));
+ return string_builder.str();
+}
+
+} // namespace utils
+} // namespace desktop_capture
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.h b/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.h
new file mode 100644
index 0000000000..ebf31419ce
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020 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 MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_CAPTURE_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_CAPTURE_UTILS_H_
+
+#include <comdef.h>
+
+#include <string>
+
+namespace webrtc {
+namespace desktop_capture {
+namespace utils {
+
+// Generates a human-readable string from a COM error.
+std::string ComErrorToString(const _com_error& error);
+
+} // namespace utils
+} // namespace desktop_capture
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_CAPTURE_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.cc b/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.cc
new file mode 100644
index 0000000000..6bc92695d9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.cc
@@ -0,0 +1,66 @@
+/*
+ * 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 "modules/desktop_capture/win/display_configuration_monitor.h"
+
+#include <windows.h>
+
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+bool DisplayConfigurationMonitor::IsChanged(
+ DesktopCapturer::SourceId source_id) {
+ DesktopRect rect = GetFullscreenRect();
+ DesktopVector dpi = GetDpiForSourceId(source_id);
+
+ if (!initialized_) {
+ initialized_ = true;
+ rect_ = rect;
+ source_dpis_.emplace(source_id, std::move(dpi));
+ return false;
+ }
+
+ if (!source_dpis_.contains(source_id)) {
+ // If this is the first time we've seen this source_id, use the current DPI
+ // so the monitor does not indicate a change and possibly get reset.
+ source_dpis_.emplace(source_id, dpi);
+ }
+
+ bool has_changed = false;
+ if (!rect.equals(rect_) || !source_dpis_.at(source_id).equals(dpi)) {
+ has_changed = true;
+ rect_ = rect;
+ source_dpis_.emplace(source_id, std::move(dpi));
+ }
+
+ return has_changed;
+}
+
+void DisplayConfigurationMonitor::Reset() {
+ initialized_ = false;
+ source_dpis_.clear();
+ rect_ = {};
+}
+
+DesktopVector DisplayConfigurationMonitor::GetDpiForSourceId(
+ DesktopCapturer::SourceId source_id) {
+ HMONITOR monitor = 0;
+ if (source_id == kFullDesktopScreenId) {
+ // Get a handle to the primary monitor when capturing the full desktop.
+ monitor = MonitorFromPoint({0, 0}, MONITOR_DEFAULTTOPRIMARY);
+ } else if (!GetHmonitorFromDeviceIndex(source_id, &monitor)) {
+ RTC_LOG(LS_WARNING) << "GetHmonitorFromDeviceIndex failed.";
+ }
+ return GetDpiForMonitor(monitor);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.h b/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.h
new file mode 100644
index 0000000000..90dfd5c28c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.h
@@ -0,0 +1,53 @@
+/*
+ * 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 MODULES_DESKTOP_CAPTURE_WIN_DISPLAY_CONFIGURATION_MONITOR_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DISPLAY_CONFIGURATION_MONITOR_H_
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/containers/flat_map.h"
+
+namespace webrtc {
+
+// A passive monitor to detect the change of display configuration on a Windows
+// system.
+// TODO(zijiehe): Also check for pixel format changes.
+class DisplayConfigurationMonitor {
+ public:
+ // Checks whether the display configuration has changed since the last time
+ // IsChanged() was called. |source_id| is used to observe changes for a
+ // specific display or all displays if kFullDesktopScreenId is passed in.
+ // Returns false if object was Reset() or if IsChanged() has not been called.
+ bool IsChanged(DesktopCapturer::SourceId source_id);
+
+ // Resets to the initial state.
+ void Reset();
+
+ private:
+ DesktopVector GetDpiForSourceId(DesktopCapturer::SourceId source_id);
+
+ // Represents the size of the desktop which includes all displays.
+ DesktopRect rect_;
+
+ // Tracks the DPI for each display being captured. We need to track for each
+ // display as each one can be configured to use a different DPI which will not
+ // be reflected in calls to get the system DPI.
+ flat_map<DesktopCapturer::SourceId, DesktopVector> source_dpis_;
+
+ // Indicates whether |rect_| and |source_dpis_| have been initialized. This is
+ // used to prevent the monitor instance from signaling 'IsChanged()' before
+ // the initial values have been set.
+ bool initialized_ = false;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DISPLAY_CONFIGURATION_MONITOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
new file mode 100644
index 0000000000..88ec4e25bf
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
@@ -0,0 +1,185 @@
+/*
+ * 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 "modules/desktop_capture/win/dxgi_adapter_duplicator.h"
+
+#include <comdef.h>
+#include <dxgi.h>
+
+#include <algorithm>
+
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+using Microsoft::WRL::ComPtr;
+
+namespace {
+
+bool IsValidRect(const RECT& rect) {
+ return rect.right > rect.left && rect.bottom > rect.top;
+}
+
+} // namespace
+
+DxgiAdapterDuplicator::DxgiAdapterDuplicator(const D3dDevice& device)
+ : device_(device) {}
+DxgiAdapterDuplicator::DxgiAdapterDuplicator(DxgiAdapterDuplicator&&) = default;
+DxgiAdapterDuplicator::~DxgiAdapterDuplicator() = default;
+
+bool DxgiAdapterDuplicator::Initialize() {
+ if (DoInitialize()) {
+ return true;
+ }
+ duplicators_.clear();
+ return false;
+}
+
+bool DxgiAdapterDuplicator::DoInitialize() {
+ for (int i = 0;; i++) {
+ ComPtr<IDXGIOutput> output;
+ _com_error error =
+ device_.dxgi_adapter()->EnumOutputs(i, output.GetAddressOf());
+ if (error.Error() == DXGI_ERROR_NOT_FOUND) {
+ break;
+ }
+
+ if (error.Error() == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
+ RTC_LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returned "
+ << "NOT_CURRENTLY_AVAILABLE. This may happen when "
+ << "running in session 0.";
+ break;
+ }
+
+ if (error.Error() != S_OK || !output) {
+ RTC_LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returned an unexpected "
+ << "result: "
+ << desktop_capture::utils::ComErrorToString(error);
+ continue;
+ }
+
+ DXGI_OUTPUT_DESC desc;
+ error = output->GetDesc(&desc);
+ if (error.Error() == S_OK) {
+ if (desc.AttachedToDesktop && IsValidRect(desc.DesktopCoordinates)) {
+ ComPtr<IDXGIOutput1> output1;
+ error = output.As(&output1);
+ if (error.Error() != S_OK || !output1) {
+ RTC_LOG(LS_WARNING)
+ << "Failed to convert IDXGIOutput to IDXGIOutput1, this usually "
+ << "means the system does not support DirectX 11";
+ continue;
+ }
+ DxgiOutputDuplicator duplicator(device_, output1, desc);
+ if (!duplicator.Initialize()) {
+ RTC_LOG(LS_WARNING) << "Failed to initialize DxgiOutputDuplicator on "
+ << "output " << i;
+ continue;
+ }
+
+ duplicators_.push_back(std::move(duplicator));
+ desktop_rect_.UnionWith(duplicators_.back().desktop_rect());
+ } else {
+ RTC_LOG(LS_ERROR) << (desc.AttachedToDesktop ? "Attached" : "Detached")
+ << " output " << i << " ("
+ << desc.DesktopCoordinates.top << ", "
+ << desc.DesktopCoordinates.left << ") - ("
+ << desc.DesktopCoordinates.bottom << ", "
+ << desc.DesktopCoordinates.right << ") is ignored.";
+ }
+ } else {
+ RTC_LOG(LS_WARNING) << "Failed to get output description of device " << i
+ << ", ignore.";
+ }
+ }
+
+ if (duplicators_.empty()) {
+ RTC_LOG(LS_WARNING)
+ << "Cannot initialize any DxgiOutputDuplicator instance.";
+ }
+
+ return !duplicators_.empty();
+}
+
+void DxgiAdapterDuplicator::Setup(Context* context) {
+ RTC_DCHECK(context->contexts.empty());
+ context->contexts.resize(duplicators_.size());
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ duplicators_[i].Setup(&context->contexts[i]);
+ }
+}
+
+void DxgiAdapterDuplicator::Unregister(const Context* const context) {
+ RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ duplicators_[i].Unregister(&context->contexts[i]);
+ }
+}
+
+bool DxgiAdapterDuplicator::Duplicate(Context* context,
+ SharedDesktopFrame* target) {
+ RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ if (!duplicators_[i].Duplicate(&context->contexts[i],
+ duplicators_[i].desktop_rect().top_left(),
+ target)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DxgiAdapterDuplicator::DuplicateMonitor(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target) {
+ RTC_DCHECK_GE(monitor_id, 0);
+ RTC_DCHECK_LT(monitor_id, duplicators_.size());
+ RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
+ return duplicators_[monitor_id].Duplicate(&context->contexts[monitor_id],
+ DesktopVector(), target);
+}
+
+DesktopRect DxgiAdapterDuplicator::ScreenRect(int id) const {
+ RTC_DCHECK_GE(id, 0);
+ RTC_DCHECK_LT(id, duplicators_.size());
+ return duplicators_[id].desktop_rect();
+}
+
+const std::string& DxgiAdapterDuplicator::GetDeviceName(int id) const {
+ RTC_DCHECK_GE(id, 0);
+ RTC_DCHECK_LT(id, duplicators_.size());
+ return duplicators_[id].device_name();
+}
+
+int DxgiAdapterDuplicator::screen_count() const {
+ return static_cast<int>(duplicators_.size());
+}
+
+int64_t DxgiAdapterDuplicator::GetNumFramesCaptured() const {
+ int64_t min = INT64_MAX;
+ for (const auto& duplicator : duplicators_) {
+ min = std::min(min, duplicator.num_frames_captured());
+ }
+
+ return min;
+}
+
+void DxgiAdapterDuplicator::TranslateRect(const DesktopVector& position) {
+ desktop_rect_.Translate(position);
+ RTC_DCHECK_GE(desktop_rect_.left(), 0);
+ RTC_DCHECK_GE(desktop_rect_.top(), 0);
+ for (auto& duplicator : duplicators_) {
+ duplicator.TranslateRect(position);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h
new file mode 100644
index 0000000000..5931b51f9e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h
@@ -0,0 +1,92 @@
+/*
+ * 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 MODULES_DESKTOP_CAPTURE_WIN_DXGI_ADAPTER_DUPLICATOR_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_ADAPTER_DUPLICATOR_H_
+
+#include <wrl/client.h>
+
+#include <vector>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/win/d3d_device.h"
+#include "modules/desktop_capture/win/dxgi_context.h"
+#include "modules/desktop_capture/win/dxgi_output_duplicator.h"
+
+namespace webrtc {
+
+// A container of DxgiOutputDuplicators to duplicate monitors attached to a
+// single video card.
+class DxgiAdapterDuplicator {
+ public:
+ using Context = DxgiAdapterContext;
+
+ // Creates an instance of DxgiAdapterDuplicator from a D3dDevice. Only
+ // DxgiDuplicatorController can create an instance.
+ explicit DxgiAdapterDuplicator(const D3dDevice& device);
+
+ // Move constructor, to make it possible to store instances of
+ // DxgiAdapterDuplicator in std::vector<>.
+ DxgiAdapterDuplicator(DxgiAdapterDuplicator&& other);
+
+ ~DxgiAdapterDuplicator();
+
+ // Initializes the DxgiAdapterDuplicator from a D3dDevice.
+ bool Initialize();
+
+ // Sequentially calls Duplicate function of all the DxgiOutputDuplicator
+ // instances owned by this instance, and writes into `target`.
+ bool Duplicate(Context* context, SharedDesktopFrame* target);
+
+ // Captures one monitor and writes into `target`. `monitor_id` should be
+ // between [0, screen_count()).
+ bool DuplicateMonitor(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target);
+
+ // Returns desktop rect covered by this DxgiAdapterDuplicator.
+ DesktopRect desktop_rect() const { return desktop_rect_; }
+
+ // Returns the size of one screen owned by this DxgiAdapterDuplicator. `id`
+ // should be between [0, screen_count()).
+ DesktopRect ScreenRect(int id) const;
+
+ // Returns the device name of one screen owned by this DxgiAdapterDuplicator
+ // in utf8 encoding. `id` should be between [0, screen_count()).
+ const std::string& GetDeviceName(int id) const;
+
+ // Returns the count of screens owned by this DxgiAdapterDuplicator. These
+ // screens can be retrieved by an interger in the range of
+ // [0, screen_count()).
+ int screen_count() const;
+
+ void Setup(Context* context);
+
+ void Unregister(const Context* const context);
+
+ // The minimum num_frames_captured() returned by `duplicators_`.
+ int64_t GetNumFramesCaptured() const;
+
+ // Moves `desktop_rect_` and all underlying `duplicators_`. See
+ // DxgiDuplicatorController::TranslateRect().
+ void TranslateRect(const DesktopVector& position);
+
+ private:
+ bool DoInitialize();
+
+ const D3dDevice device_;
+ std::vector<DxgiOutputDuplicator> duplicators_;
+ DesktopRect desktop_rect_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_ADAPTER_DUPLICATOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.cc
new file mode 100644
index 0000000000..c18b238f03
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.cc
@@ -0,0 +1,33 @@
+/*
+ * 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 "modules/desktop_capture/win/dxgi_context.h"
+
+#include "modules/desktop_capture/win/dxgi_duplicator_controller.h"
+
+namespace webrtc {
+
+DxgiAdapterContext::DxgiAdapterContext() = default;
+DxgiAdapterContext::DxgiAdapterContext(const DxgiAdapterContext& context) =
+ default;
+DxgiAdapterContext::~DxgiAdapterContext() = default;
+
+DxgiFrameContext::DxgiFrameContext() = default;
+
+DxgiFrameContext::~DxgiFrameContext() {
+ Reset();
+}
+
+void DxgiFrameContext::Reset() {
+ DxgiDuplicatorController::Instance()->Unregister(this);
+ controller_id = 0;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.h
new file mode 100644
index 0000000000..59c2112db5
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.h
@@ -0,0 +1,62 @@
+/*
+ * 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 MODULES_DESKTOP_CAPTURE_WIN_DXGI_CONTEXT_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_CONTEXT_H_
+
+#include <vector>
+
+#include "modules/desktop_capture/desktop_region.h"
+
+namespace webrtc {
+
+// A DxgiOutputContext stores the status of a single DxgiFrame of
+// DxgiOutputDuplicator.
+struct DxgiOutputContext final {
+ // The updated region DxgiOutputDuplicator::DetectUpdatedRegion() output
+ // during last Duplicate() function call. It's always relative to the (0, 0).
+ DesktopRegion updated_region;
+};
+
+// A DxgiAdapterContext stores the status of a single DxgiFrame of
+// DxgiAdapterDuplicator.
+struct DxgiAdapterContext final {
+ DxgiAdapterContext();
+ DxgiAdapterContext(const DxgiAdapterContext& other);
+ ~DxgiAdapterContext();
+
+ // Child DxgiOutputContext belongs to this AdapterContext.
+ std::vector<DxgiOutputContext> contexts;
+};
+
+// A DxgiFrameContext stores the status of a single DxgiFrame of
+// DxgiDuplicatorController.
+struct DxgiFrameContext final {
+ public:
+ DxgiFrameContext();
+ // Unregister this Context instance from DxgiDuplicatorController during
+ // destructing.
+ ~DxgiFrameContext();
+
+ // Reset current Context, so it will be reinitialized next time.
+ void Reset();
+
+ // A Context will have an exactly same `controller_id` as
+ // DxgiDuplicatorController, to ensure it has been correctly setted up after
+ // each DxgiDuplicatorController::Initialize().
+ int controller_id = 0;
+
+ // Child DxgiAdapterContext belongs to this DxgiFrameContext.
+ std::vector<DxgiAdapterContext> contexts;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_CONTEXT_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc
new file mode 100644
index 0000000000..973aa3fd99
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc
@@ -0,0 +1,515 @@
+/*
+ * 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 "modules/desktop_capture/win/dxgi_duplicator_controller.h"
+
+#include <windows.h>
+
+#include <algorithm>
+#include <string>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/win/dxgi_frame.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "system_wrappers/include/sleep.h"
+
+namespace webrtc {
+
+namespace {
+
+constexpr DWORD kInvalidSessionId = 0xFFFFFFFF;
+
+DWORD GetCurrentSessionId() {
+ DWORD session_id = kInvalidSessionId;
+ if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id)) {
+ RTC_LOG(LS_WARNING)
+ << "Failed to retrieve current session Id, current binary "
+ "may not have required priviledge.";
+ }
+ return session_id;
+}
+
+bool IsConsoleSession() {
+ return WTSGetActiveConsoleSessionId() == GetCurrentSessionId();
+}
+
+} // namespace
+
+// static
+std::string DxgiDuplicatorController::ResultName(
+ DxgiDuplicatorController::Result result) {
+ switch (result) {
+ case Result::SUCCEEDED:
+ return "Succeeded";
+ case Result::UNSUPPORTED_SESSION:
+ return "Unsupported session";
+ case Result::FRAME_PREPARE_FAILED:
+ return "Frame preparation failed";
+ case Result::INITIALIZATION_FAILED:
+ return "Initialization failed";
+ case Result::DUPLICATION_FAILED:
+ return "Duplication failed";
+ case Result::INVALID_MONITOR_ID:
+ return "Invalid monitor id";
+ default:
+ return "Unknown error";
+ }
+}
+
+// static
+rtc::scoped_refptr<DxgiDuplicatorController>
+DxgiDuplicatorController::Instance() {
+ // The static instance won't be deleted to ensure it can be used by other
+ // threads even during program exiting.
+ static DxgiDuplicatorController* instance = new DxgiDuplicatorController();
+ return rtc::scoped_refptr<DxgiDuplicatorController>(instance);
+}
+
+// static
+bool DxgiDuplicatorController::IsCurrentSessionSupported() {
+ DWORD current_session_id = GetCurrentSessionId();
+ return current_session_id != kInvalidSessionId && current_session_id != 0;
+}
+
+DxgiDuplicatorController::DxgiDuplicatorController() : refcount_(0) {}
+
+void DxgiDuplicatorController::AddRef() {
+ int refcount = (++refcount_);
+ RTC_DCHECK(refcount > 0);
+}
+
+void DxgiDuplicatorController::Release() {
+ int refcount = (--refcount_);
+ RTC_DCHECK(refcount >= 0);
+ if (refcount == 0) {
+ RTC_LOG(LS_WARNING) << "Count of references reaches zero, "
+ "DxgiDuplicatorController will be unloaded.";
+ Unload();
+ }
+}
+
+bool DxgiDuplicatorController::IsSupported() {
+ MutexLock lock(&mutex_);
+ return Initialize();
+}
+
+bool DxgiDuplicatorController::RetrieveD3dInfo(D3dInfo* info) {
+ bool result = false;
+ {
+ MutexLock lock(&mutex_);
+ result = Initialize();
+ *info = d3d_info_;
+ }
+ if (!result) {
+ RTC_LOG(LS_WARNING) << "Failed to initialize DXGI components, the D3dInfo "
+ "retrieved may not accurate or out of date.";
+ }
+ return result;
+}
+
+DxgiDuplicatorController::Result DxgiDuplicatorController::Duplicate(
+ DxgiFrame* frame) {
+ return DoDuplicate(frame, -1);
+}
+
+DxgiDuplicatorController::Result DxgiDuplicatorController::DuplicateMonitor(
+ DxgiFrame* frame,
+ int monitor_id) {
+ RTC_DCHECK_GE(monitor_id, 0);
+ return DoDuplicate(frame, monitor_id);
+}
+
+DesktopVector DxgiDuplicatorController::system_dpi() {
+ MutexLock lock(&mutex_);
+ if (Initialize()) {
+ return system_dpi_;
+ }
+ return DesktopVector();
+}
+
+int DxgiDuplicatorController::ScreenCount() {
+ MutexLock lock(&mutex_);
+ if (Initialize()) {
+ return ScreenCountUnlocked();
+ }
+ return 0;
+}
+
+bool DxgiDuplicatorController::GetDeviceNames(
+ std::vector<std::string>* output) {
+ MutexLock lock(&mutex_);
+ if (Initialize()) {
+ GetDeviceNamesUnlocked(output);
+ return true;
+ }
+ return false;
+}
+
+DxgiDuplicatorController::Result DxgiDuplicatorController::DoDuplicate(
+ DxgiFrame* frame,
+ int monitor_id) {
+ RTC_DCHECK(frame);
+ MutexLock lock(&mutex_);
+
+ // The dxgi components and APIs do not update the screen resolution without
+ // a reinitialization. So we use the GetDC() function to retrieve the screen
+ // resolution to decide whether dxgi components need to be reinitialized.
+ // If the screen resolution changed, it's very likely the next Duplicate()
+ // function call will fail because of a missing monitor or the frame size is
+ // not enough to store the output. So we reinitialize dxgi components in-place
+ // to avoid a capture failure.
+ // But there is no guarantee GetDC() function returns the same resolution as
+ // dxgi APIs, we still rely on dxgi components to return the output frame
+ // size.
+ // TODO(zijiehe): Confirm whether IDXGIOutput::GetDesc() and
+ // IDXGIOutputDuplication::GetDesc() can detect the resolution change without
+ // reinitialization.
+ if (display_configuration_monitor_.IsChanged(frame->source_id_)) {
+ Deinitialize();
+ }
+
+ if (!Initialize()) {
+ if (succeeded_duplications_ == 0 && !IsCurrentSessionSupported()) {
+ RTC_LOG(LS_WARNING) << "Current binary is running in session 0. DXGI "
+ "components cannot be initialized.";
+ return Result::UNSUPPORTED_SESSION;
+ }
+
+ // Cannot initialize COM components now, display mode may be changing.
+ return Result::INITIALIZATION_FAILED;
+ }
+
+ if (!frame->Prepare(SelectedDesktopSize(monitor_id), monitor_id)) {
+ return Result::FRAME_PREPARE_FAILED;
+ }
+
+ frame->frame()->mutable_updated_region()->Clear();
+
+ if (DoDuplicateUnlocked(frame->context(), monitor_id, frame->frame())) {
+ succeeded_duplications_++;
+ return Result::SUCCEEDED;
+ }
+ if (monitor_id >= ScreenCountUnlocked()) {
+ // It's a user error to provide a `monitor_id` larger than screen count. We
+ // do not need to deinitialize.
+ return Result::INVALID_MONITOR_ID;
+ }
+
+ // If the `monitor_id` is valid, but DoDuplicateUnlocked() failed, something
+ // must be wrong from capturer APIs. We should Deinitialize().
+ Deinitialize();
+ return Result::DUPLICATION_FAILED;
+}
+
+void DxgiDuplicatorController::Unload() {
+ MutexLock lock(&mutex_);
+ Deinitialize();
+}
+
+void DxgiDuplicatorController::Unregister(const Context* const context) {
+ MutexLock lock(&mutex_);
+ if (ContextExpired(context)) {
+ // The Context has not been setup after a recent initialization, so it
+ // should not been registered in duplicators.
+ return;
+ }
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ duplicators_[i].Unregister(&context->contexts[i]);
+ }
+}
+
+bool DxgiDuplicatorController::Initialize() {
+ if (!duplicators_.empty()) {
+ return true;
+ }
+
+ if (DoInitialize()) {
+ return true;
+ }
+ Deinitialize();
+ return false;
+}
+
+bool DxgiDuplicatorController::DoInitialize() {
+ RTC_DCHECK(desktop_rect_.is_empty());
+ RTC_DCHECK(duplicators_.empty());
+
+ d3d_info_.min_feature_level = static_cast<D3D_FEATURE_LEVEL>(0);
+ d3d_info_.max_feature_level = static_cast<D3D_FEATURE_LEVEL>(0);
+
+ std::vector<D3dDevice> devices = D3dDevice::EnumDevices();
+ if (devices.empty()) {
+ RTC_LOG(LS_WARNING) << "No D3dDevice found.";
+ return false;
+ }
+
+ for (size_t i = 0; i < devices.size(); i++) {
+ D3D_FEATURE_LEVEL feature_level =
+ devices[i].d3d_device()->GetFeatureLevel();
+ if (d3d_info_.max_feature_level == 0 ||
+ feature_level > d3d_info_.max_feature_level) {
+ d3d_info_.max_feature_level = feature_level;
+ }
+ if (d3d_info_.min_feature_level == 0 ||
+ feature_level < d3d_info_.min_feature_level) {
+ d3d_info_.min_feature_level = feature_level;
+ }
+
+ DxgiAdapterDuplicator duplicator(devices[i]);
+ // There may be several video cards on the system, some of them may not
+ // support IDXGOutputDuplication. But they should not impact others from
+ // taking effect, so we should continually try other adapters. This usually
+ // happens when a non-official virtual adapter is installed on the system.
+ if (!duplicator.Initialize()) {
+ RTC_LOG(LS_WARNING) << "Failed to initialize DxgiAdapterDuplicator on "
+ "adapter "
+ << i;
+ continue;
+ }
+ RTC_DCHECK(!duplicator.desktop_rect().is_empty());
+ duplicators_.push_back(std::move(duplicator));
+
+ desktop_rect_.UnionWith(duplicators_.back().desktop_rect());
+ }
+ TranslateRect();
+
+ HDC hdc = GetDC(nullptr);
+ // Use old DPI value if failed.
+ if (hdc) {
+ system_dpi_.set(GetDeviceCaps(hdc, LOGPIXELSX),
+ GetDeviceCaps(hdc, LOGPIXELSY));
+ ReleaseDC(nullptr, hdc);
+ }
+
+ identity_++;
+
+ if (duplicators_.empty()) {
+ RTC_LOG(LS_WARNING)
+ << "Cannot initialize any DxgiAdapterDuplicator instance.";
+ }
+
+ return !duplicators_.empty();
+}
+
+void DxgiDuplicatorController::Deinitialize() {
+ desktop_rect_ = DesktopRect();
+ duplicators_.clear();
+ display_configuration_monitor_.Reset();
+}
+
+bool DxgiDuplicatorController::ContextExpired(
+ const Context* const context) const {
+ RTC_DCHECK(context);
+ return context->controller_id != identity_ ||
+ context->contexts.size() != duplicators_.size();
+}
+
+void DxgiDuplicatorController::Setup(Context* context) {
+ if (ContextExpired(context)) {
+ RTC_DCHECK(context);
+ context->contexts.clear();
+ context->contexts.resize(duplicators_.size());
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ duplicators_[i].Setup(&context->contexts[i]);
+ }
+ context->controller_id = identity_;
+ }
+}
+
+bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target) {
+ Setup(context);
+
+ if (!EnsureFrameCaptured(context, target)) {
+ return false;
+ }
+
+ bool result = false;
+ if (monitor_id < 0) {
+ // Capture entire screen.
+ result = DoDuplicateAll(context, target);
+ } else {
+ result = DoDuplicateOne(context, monitor_id, target);
+ }
+
+ if (result) {
+ target->set_dpi(system_dpi_);
+ return true;
+ }
+
+ return false;
+}
+
+bool DxgiDuplicatorController::DoDuplicateAll(Context* context,
+ SharedDesktopFrame* target) {
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ if (!duplicators_[i].Duplicate(&context->contexts[i], target)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DxgiDuplicatorController::DoDuplicateOne(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target) {
+ RTC_DCHECK(monitor_id >= 0);
+ for (size_t i = 0; i < duplicators_.size() && i < context->contexts.size();
+ i++) {
+ if (monitor_id >= duplicators_[i].screen_count()) {
+ monitor_id -= duplicators_[i].screen_count();
+ } else {
+ if (duplicators_[i].DuplicateMonitor(&context->contexts[i], monitor_id,
+ target)) {
+ target->set_top_left(duplicators_[i].ScreenRect(monitor_id).top_left());
+ return true;
+ }
+ return false;
+ }
+ }
+ return false;
+}
+
+int64_t DxgiDuplicatorController::GetNumFramesCaptured() const {
+ int64_t min = INT64_MAX;
+ for (const auto& duplicator : duplicators_) {
+ min = std::min(min, duplicator.GetNumFramesCaptured());
+ }
+
+ return min;
+}
+
+DesktopSize DxgiDuplicatorController::desktop_size() const {
+ return desktop_rect_.size();
+}
+
+DesktopRect DxgiDuplicatorController::ScreenRect(int id) const {
+ RTC_DCHECK(id >= 0);
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ if (id >= duplicators_[i].screen_count()) {
+ id -= duplicators_[i].screen_count();
+ } else {
+ return duplicators_[i].ScreenRect(id);
+ }
+ }
+ return DesktopRect();
+}
+
+int DxgiDuplicatorController::ScreenCountUnlocked() const {
+ int result = 0;
+ for (auto& duplicator : duplicators_) {
+ result += duplicator.screen_count();
+ }
+ return result;
+}
+
+void DxgiDuplicatorController::GetDeviceNamesUnlocked(
+ std::vector<std::string>* output) const {
+ RTC_DCHECK(output);
+ for (auto& duplicator : duplicators_) {
+ for (int i = 0; i < duplicator.screen_count(); i++) {
+ output->push_back(duplicator.GetDeviceName(i));
+ }
+ }
+}
+
+DesktopSize DxgiDuplicatorController::SelectedDesktopSize(
+ int monitor_id) const {
+ if (monitor_id < 0) {
+ return desktop_size();
+ }
+
+ return ScreenRect(monitor_id).size();
+}
+
+bool DxgiDuplicatorController::EnsureFrameCaptured(Context* context,
+ SharedDesktopFrame* target) {
+ // On a modern system, the FPS / monitor refresh rate is usually larger than
+ // or equal to 60. So 17 milliseconds is enough to capture at least one frame.
+ const int64_t ms_per_frame = 17;
+ // Skip frames to ensure a full frame refresh has occurred and the DXGI
+ // machinery is producing frames before this function returns.
+ int64_t frames_to_skip = 1;
+ // The total time out milliseconds for this function. If we cannot get enough
+ // frames during this time interval, this function returns false, and cause
+ // the DXGI components to be reinitialized. This usually should not happen
+ // unless the system is switching display mode when this function is being
+ // called. 500 milliseconds should be enough for ~30 frames.
+ const int64_t timeout_ms = 500;
+
+ if (GetNumFramesCaptured() == 0 && !IsConsoleSession()) {
+ // When capturing a console session, waiting for a single frame is
+ // sufficient to ensure that DXGI output duplication is working. When the
+ // session is not attached to the console, it has been observed that DXGI
+ // may produce up to 4 frames (typically 1-2 though) before stopping. When
+ // this condition occurs, no errors are returned from the output duplication
+ // API, it simply appears that nothing is changing on the screen. Thus for
+ // detached sessions, we need to capture a few extra frames before we can be
+ // confident that output duplication was initialized properly.
+ frames_to_skip = 5;
+ }
+
+ if (GetNumFramesCaptured() >= frames_to_skip) {
+ return true;
+ }
+
+ std::unique_ptr<SharedDesktopFrame> fallback_frame;
+ SharedDesktopFrame* shared_frame = nullptr;
+ if (target->size().width() >= desktop_size().width() &&
+ target->size().height() >= desktop_size().height()) {
+ // `target` is large enough to cover entire screen, we do not need to use
+ // `fallback_frame`.
+ shared_frame = target;
+ } else {
+ fallback_frame = SharedDesktopFrame::Wrap(
+ std::unique_ptr<DesktopFrame>(new BasicDesktopFrame(desktop_size())));
+ shared_frame = fallback_frame.get();
+ }
+
+ const int64_t start_ms = rtc::TimeMillis();
+ while (GetNumFramesCaptured() < frames_to_skip) {
+ if (!DoDuplicateAll(context, shared_frame)) {
+ return false;
+ }
+
+ // Calling DoDuplicateAll() may change the number of frames captured.
+ if (GetNumFramesCaptured() >= frames_to_skip) {
+ break;
+ }
+
+ if (rtc::TimeMillis() - start_ms > timeout_ms) {
+ RTC_LOG(LS_ERROR) << "Failed to capture " << frames_to_skip
+ << " frames "
+ "within "
+ << timeout_ms << " milliseconds.";
+ return false;
+ }
+
+ // Sleep `ms_per_frame` before attempting to capture the next frame to
+ // ensure the video adapter has time to update the screen.
+ webrtc::SleepMs(ms_per_frame);
+ }
+ return true;
+}
+
+void DxgiDuplicatorController::TranslateRect() {
+ const DesktopVector position =
+ DesktopVector().subtract(desktop_rect_.top_left());
+ desktop_rect_.Translate(position);
+ for (auto& duplicator : duplicators_) {
+ duplicator.TranslateRect(position);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h
new file mode 100644
index 0000000000..2b1e0ab041
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h
@@ -0,0 +1,257 @@
+/*
+ * 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 MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_
+
+#include <d3dcommon.h>
+
+#include <atomic>
+#include <string>
+#include <vector>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/win/d3d_device.h"
+#include "modules/desktop_capture/win/display_configuration_monitor.h"
+#include "modules/desktop_capture/win/dxgi_adapter_duplicator.h"
+#include "modules/desktop_capture/win/dxgi_context.h"
+#include "modules/desktop_capture/win/dxgi_frame.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// A controller for all the objects we need to call Windows DirectX capture APIs
+// It's a singleton because only one IDXGIOutputDuplication instance per monitor
+// is allowed per application.
+//
+// Consumers should create a DxgiDuplicatorController::Context and keep it
+// throughout their lifetime, and pass it when calling Duplicate(). Consumers
+// can also call IsSupported() to determine whether the system supports DXGI
+// duplicator or not. If a previous IsSupported() function call returns true,
+// but a later Duplicate() returns false, this usually means the display mode is
+// changing. Consumers should retry after a while. (Typically 50 milliseconds,
+// but according to hardware performance, this time may vary.)
+// The underlying DxgiOutputDuplicators may take an additional reference on the
+// frame passed in to the Duplicate methods so that they can guarantee delivery
+// of new frames when requested; since if there have been no updates to the
+// surface, they may be unable to capture a frame.
+class RTC_EXPORT DxgiDuplicatorController {
+ public:
+ using Context = DxgiFrameContext;
+
+ // A collection of D3d information we are interested in, which may impact
+ // capturer performance or reliability.
+ struct D3dInfo {
+ // Each video adapter has its own D3D_FEATURE_LEVEL, so this structure
+ // contains the minimum and maximium D3D_FEATURE_LEVELs current system
+ // supports.
+ // Both fields can be 0, which is the default value to indicate no valid
+ // D3D_FEATURE_LEVEL has been retrieved from underlying OS APIs.
+ D3D_FEATURE_LEVEL min_feature_level;
+ D3D_FEATURE_LEVEL max_feature_level;
+
+ // TODO(zijiehe): Add more fields, such as manufacturer name, mode, driver
+ // version.
+ };
+
+ // These values are persisted to logs. Entries should not be renumbered or
+ // reordered and numeric values should never be reused. This enum corresponds
+ // to WebRtcDirectXCapturerResult in tools/metrics/histograms/enums.xml.
+ enum class Result {
+ SUCCEEDED = 0,
+ UNSUPPORTED_SESSION = 1,
+ FRAME_PREPARE_FAILED = 2,
+ INITIALIZATION_FAILED = 3,
+ DUPLICATION_FAILED = 4,
+ INVALID_MONITOR_ID = 5,
+ MAX_VALUE = INVALID_MONITOR_ID
+ };
+
+ // Converts `result` into user-friendly string representation. The return
+ // value should not be used to identify error types.
+ static std::string ResultName(Result result);
+
+ // Returns the singleton instance of DxgiDuplicatorController.
+ static rtc::scoped_refptr<DxgiDuplicatorController> Instance();
+
+ // See ScreenCapturerWinDirectx::IsCurrentSessionSupported().
+ static bool IsCurrentSessionSupported();
+
+ // All the following public functions implicitly call Initialize() function.
+
+ // Detects whether the system supports DXGI based capturer.
+ bool IsSupported();
+
+ // Returns a copy of D3dInfo composed by last Initialize() function call. This
+ // function always copies the latest information into `info`. But once the
+ // function returns false, the information in `info` may not accurate.
+ bool RetrieveD3dInfo(D3dInfo* info);
+
+ // Captures current screen and writes into `frame`. May retain a reference to
+ // `frame`'s underlying |SharedDesktopFrame|.
+ // TODO(zijiehe): Windows cannot guarantee the frames returned by each
+ // IDXGIOutputDuplication are synchronized. But we are using a totally
+ // different threading model than the way Windows suggested, it's hard to
+ // synchronize them manually. We should find a way to do it.
+ Result Duplicate(DxgiFrame* frame);
+
+ // Captures one monitor and writes into target. `monitor_id` must be >= 0. If
+ // `monitor_id` is greater than the total screen count of all the Duplicators,
+ // this function returns false. May retain a reference to `frame`'s underlying
+ // |SharedDesktopFrame|.
+ Result DuplicateMonitor(DxgiFrame* frame, int monitor_id);
+
+ // Returns dpi of current system. Returns an empty DesktopVector if system
+ // does not support DXGI based capturer.
+ DesktopVector system_dpi();
+
+ // Returns the count of screens on the system. These screens can be retrieved
+ // by an integer in the range of [0, ScreenCount()). If system does not
+ // support DXGI based capturer, this function returns 0.
+ int ScreenCount();
+
+ // Returns the device names of all screens on the system in utf8 encoding.
+ // These screens can be retrieved by an integer in the range of
+ // [0, output->size()). If system does not support DXGI based capturer, this
+ // function returns false.
+ bool GetDeviceNames(std::vector<std::string>* output);
+
+ private:
+ // DxgiFrameContext calls private Unregister(Context*) function in Reset().
+ friend void DxgiFrameContext::Reset();
+
+ // scoped_refptr<DxgiDuplicatorController> accesses private AddRef() and
+ // Release() functions.
+ friend class rtc::scoped_refptr<DxgiDuplicatorController>;
+
+ // A private constructor to ensure consumers to use
+ // DxgiDuplicatorController::Instance().
+ DxgiDuplicatorController();
+
+ // Not implemented: The singleton DxgiDuplicatorController instance should not
+ // be deleted.
+ ~DxgiDuplicatorController();
+
+ // RefCountedInterface implementations.
+ void AddRef();
+ void Release();
+
+ // Does the real duplication work. Setting `monitor_id` < 0 to capture entire
+ // screen. This function calls Initialize(). And if the duplication failed,
+ // this function calls Deinitialize() to ensure the Dxgi components can be
+ // reinitialized next time.
+ Result DoDuplicate(DxgiFrame* frame, int monitor_id);
+
+ // Unload all the DXGI components and releases the resources. This function
+ // wraps Deinitialize() with `mutex_`.
+ void Unload();
+
+ // Unregisters Context from this instance and all DxgiAdapterDuplicator(s)
+ // it owns.
+ void Unregister(const Context* const context);
+
+ // All functions below should be called in `mutex_` locked scope and should be
+ // after a successful Initialize().
+
+ // If current instance has not been initialized, executes DoInitialize()
+ // function, and returns initialize result. Otherwise directly returns true.
+ // This function may calls Deinitialize() if initialization failed.
+ bool Initialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Does the real initialization work, this function should only be called in
+ // Initialize().
+ bool DoInitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Clears all COM components referred to by this instance. So next Duplicate()
+ // call will eventually initialize this instance again.
+ void Deinitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // A helper function to check whether a Context has been expired.
+ bool ContextExpired(const Context* const context) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Updates Context if needed.
+ void Setup(Context* context) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ bool DoDuplicateUnlocked(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Captures all monitors.
+ bool DoDuplicateAll(Context* context, SharedDesktopFrame* target)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Captures one monitor.
+ bool DoDuplicateOne(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // The minimum GetNumFramesCaptured() returned by `duplicators_`.
+ int64_t GetNumFramesCaptured() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Returns a DesktopSize to cover entire `desktop_rect_`.
+ DesktopSize desktop_size() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Returns the size of one screen. `id` should be >= 0. If system does not
+ // support DXGI based capturer, or `id` is greater than the total screen count
+ // of all the Duplicators, this function returns an empty DesktopRect.
+ DesktopRect ScreenRect(int id) const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ int ScreenCountUnlocked() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ void GetDeviceNamesUnlocked(std::vector<std::string>* output) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Returns the desktop size of the selected screen `monitor_id`. Setting
+ // `monitor_id` < 0 to return the entire screen size.
+ DesktopSize SelectedDesktopSize(int monitor_id) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Retries DoDuplicateAll() for several times until GetNumFramesCaptured() is
+ // large enough. Returns false if DoDuplicateAll() returns false, or
+ // GetNumFramesCaptured() has never reached the requirement.
+ // According to http://crbug.com/682112, dxgi capturer returns a black frame
+ // during first several capture attempts.
+ bool EnsureFrameCaptured(Context* context, SharedDesktopFrame* target)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Moves `desktop_rect_` and all underlying `duplicators_`, putting top left
+ // corner of the desktop at (0, 0). This is necessary because DXGI_OUTPUT_DESC
+ // may return negative coordinates. Called from DoInitialize() after all
+ // DxgiAdapterDuplicator and DxgiOutputDuplicator instances are initialized.
+ void TranslateRect() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // The count of references which are now "living".
+ std::atomic_int refcount_;
+
+ // This lock must be locked whenever accessing any of the following objects.
+ Mutex mutex_;
+
+ // A self-incremented integer to compare with the one in Context. It ensures
+ // a Context instance is always initialized after DxgiDuplicatorController.
+ int identity_ RTC_GUARDED_BY(mutex_) = 0;
+ DesktopRect desktop_rect_ RTC_GUARDED_BY(mutex_);
+ DesktopVector system_dpi_ RTC_GUARDED_BY(mutex_);
+ std::vector<DxgiAdapterDuplicator> duplicators_ RTC_GUARDED_BY(mutex_);
+ D3dInfo d3d_info_ RTC_GUARDED_BY(mutex_);
+ DisplayConfigurationMonitor display_configuration_monitor_
+ RTC_GUARDED_BY(mutex_);
+ // A number to indicate how many successful duplications have been performed.
+ uint32_t succeeded_duplications_ RTC_GUARDED_BY(mutex_) = 0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.cc
new file mode 100644
index 0000000000..13d5b4b62e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.cc
@@ -0,0 +1,77 @@
+/*
+ * 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 "modules/desktop_capture/win/dxgi_frame.h"
+
+#include <string.h>
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/win/dxgi_duplicator_controller.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+DxgiFrame::DxgiFrame(SharedMemoryFactory* factory) : factory_(factory) {}
+
+DxgiFrame::~DxgiFrame() = default;
+
+bool DxgiFrame::Prepare(DesktopSize size, DesktopCapturer::SourceId source_id) {
+ if (source_id != source_id_) {
+ // Once the source has been changed, the entire source should be copied.
+ source_id_ = source_id;
+ context_.Reset();
+ }
+
+ if (resolution_tracker_.SetResolution(size)) {
+ // Once the output size changed, recreate the SharedDesktopFrame.
+ frame_.reset();
+ }
+
+ if (!frame_) {
+ std::unique_ptr<DesktopFrame> frame;
+ if (factory_) {
+ frame = SharedMemoryDesktopFrame::Create(size, factory_);
+
+ if (!frame) {
+ RTC_LOG(LS_WARNING) << "DxgiFrame cannot create a new DesktopFrame.";
+ return false;
+ }
+
+ // DirectX capturer won't paint each pixel in the frame due to its one
+ // capturer per monitor design. So once the new frame is created, we
+ // should clear it to avoid the legacy image to be remained on it. See
+ // http://crbug.com/708766.
+ RTC_DCHECK_EQ(frame->stride(),
+ frame->size().width() * DesktopFrame::kBytesPerPixel);
+ memset(frame->data(), 0, frame->stride() * frame->size().height());
+ } else {
+ frame.reset(new BasicDesktopFrame(size));
+ }
+
+ frame_ = SharedDesktopFrame::Wrap(std::move(frame));
+ }
+
+ return !!frame_;
+}
+
+SharedDesktopFrame* DxgiFrame::frame() const {
+ RTC_DCHECK(frame_);
+ return frame_.get();
+}
+
+DxgiFrame::Context* DxgiFrame::context() {
+ RTC_DCHECK(frame_);
+ return &context_;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.h
new file mode 100644
index 0000000000..6a9ce868a7
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.h
@@ -0,0 +1,63 @@
+/*
+ * 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 MODULES_DESKTOP_CAPTURE_WIN_DXGI_FRAME_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_FRAME_H_
+
+#include <memory>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/resolution_tracker.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/shared_memory.h"
+#include "modules/desktop_capture/win/dxgi_context.h"
+
+namespace webrtc {
+
+class DxgiDuplicatorController;
+
+// A pair of a SharedDesktopFrame and a DxgiDuplicatorController::Context for
+// the client of DxgiDuplicatorController.
+class DxgiFrame final {
+ public:
+ using Context = DxgiFrameContext;
+
+ // DxgiFrame does not take ownership of `factory`, consumers should ensure it
+ // outlives this instance. nullptr is acceptable.
+ explicit DxgiFrame(SharedMemoryFactory* factory);
+ ~DxgiFrame();
+
+ // Should not be called if Prepare() is not executed or returns false.
+ SharedDesktopFrame* frame() const;
+
+ private:
+ // Allows DxgiDuplicatorController to access Prepare() and context() function
+ // as well as Context class.
+ friend class DxgiDuplicatorController;
+
+ // Prepares current instance with desktop size and source id.
+ bool Prepare(DesktopSize size, DesktopCapturer::SourceId source_id);
+
+ // Should not be called if Prepare() is not executed or returns false.
+ Context* context();
+
+ SharedMemoryFactory* const factory_;
+ ResolutionTracker resolution_tracker_;
+ DesktopCapturer::SourceId source_id_ = kFullDesktopScreenId;
+ std::unique_ptr<SharedDesktopFrame> frame_;
+ Context context_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_FRAME_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc
new file mode 100644
index 0000000000..9c64125b4e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc
@@ -0,0 +1,423 @@
+/*
+ * 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 "modules/desktop_capture/win/dxgi_output_duplicator.h"
+
+#include <dxgi.h>
+#include <dxgiformat.h>
+#include <string.h>
+#include <unknwn.h>
+#include <windows.h>
+
+#include <algorithm>
+
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "modules/desktop_capture/win/dxgi_texture_mapping.h"
+#include "modules/desktop_capture/win/dxgi_texture_staging.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/string_utils.h"
+#include "rtc_base/win32.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+using Microsoft::WRL::ComPtr;
+
+namespace {
+
+// Timeout for AcquireNextFrame() call.
+// DxgiDuplicatorController leverages external components to do the capture
+// scheduling. So here DxgiOutputDuplicator does not need to actively wait for a
+// new frame.
+const int kAcquireTimeoutMs = 0;
+
+DesktopRect RECTToDesktopRect(const RECT& rect) {
+ return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+Rotation DxgiRotationToRotation(DXGI_MODE_ROTATION rotation) {
+ switch (rotation) {
+ case DXGI_MODE_ROTATION_IDENTITY:
+ case DXGI_MODE_ROTATION_UNSPECIFIED:
+ return Rotation::CLOCK_WISE_0;
+ case DXGI_MODE_ROTATION_ROTATE90:
+ return Rotation::CLOCK_WISE_90;
+ case DXGI_MODE_ROTATION_ROTATE180:
+ return Rotation::CLOCK_WISE_180;
+ case DXGI_MODE_ROTATION_ROTATE270:
+ return Rotation::CLOCK_WISE_270;
+ }
+
+ RTC_DCHECK_NOTREACHED();
+ return Rotation::CLOCK_WISE_0;
+}
+
+} // namespace
+
+DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device,
+ const ComPtr<IDXGIOutput1>& output,
+ const DXGI_OUTPUT_DESC& desc)
+ : device_(device),
+ output_(output),
+ device_name_(rtc::ToUtf8(desc.DeviceName)),
+ desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) {
+ RTC_DCHECK(output_);
+ RTC_DCHECK(!desktop_rect_.is_empty());
+ RTC_DCHECK_GT(desktop_rect_.width(), 0);
+ RTC_DCHECK_GT(desktop_rect_.height(), 0);
+}
+
+DxgiOutputDuplicator::DxgiOutputDuplicator(DxgiOutputDuplicator&& other) =
+ default;
+
+DxgiOutputDuplicator::~DxgiOutputDuplicator() {
+ if (duplication_) {
+ duplication_->ReleaseFrame();
+ }
+ texture_.reset();
+}
+
+bool DxgiOutputDuplicator::Initialize() {
+ if (DuplicateOutput()) {
+ if (desc_.DesktopImageInSystemMemory) {
+ texture_.reset(new DxgiTextureMapping(duplication_.Get()));
+ } else {
+ texture_.reset(new DxgiTextureStaging(device_));
+ }
+ return true;
+ } else {
+ duplication_.Reset();
+ return false;
+ }
+}
+
+bool DxgiOutputDuplicator::DuplicateOutput() {
+ RTC_DCHECK(!duplication_);
+ _com_error error =
+ output_->DuplicateOutput(static_cast<IUnknown*>(device_.d3d_device()),
+ duplication_.GetAddressOf());
+ if (error.Error() != S_OK || !duplication_) {
+ RTC_LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ memset(&desc_, 0, sizeof(desc_));
+ duplication_->GetDesc(&desc_);
+ if (desc_.ModeDesc.Format != DXGI_FORMAT_B8G8R8A8_UNORM) {
+ RTC_LOG(LS_ERROR) << "IDXGIDuplicateOutput does not use RGBA (8 bit) "
+ << "format, which is required by downstream components, "
+ << "format is " << desc_.ModeDesc.Format;
+ return false;
+ }
+
+ if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() ||
+ static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) {
+ RTC_LOG(LS_ERROR)
+ << "IDXGIDuplicateOutput does not return a same size as its "
+ << "IDXGIOutput1, size returned by IDXGIDuplicateOutput is "
+ << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height
+ << ", size returned by IDXGIOutput1 is " << desktop_rect_.width()
+ << " x " << desktop_rect_.height();
+ return false;
+ }
+
+ rotation_ = DxgiRotationToRotation(desc_.Rotation);
+ unrotated_size_ = RotateSize(desktop_size(), ReverseRotation(rotation_));
+
+ return true;
+}
+
+bool DxgiOutputDuplicator::ReleaseFrame() {
+ RTC_DCHECK(duplication_);
+ _com_error error = duplication_->ReleaseFrame();
+ if (error.Error() != S_OK) {
+ RTC_LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+ return true;
+}
+
+bool DxgiOutputDuplicator::ContainsMouseCursor(
+ const DXGI_OUTDUPL_FRAME_INFO& frame_info) {
+ // The DXGI_OUTDUPL_POINTER_POSITION structure that describes the most recent
+ // mouse position is only valid if the LastMouseUpdateTime member is a non-
+ // zero value.
+ if (frame_info.LastMouseUpdateTime.QuadPart == 0)
+ return false;
+
+ // Ignore cases when the mouse shape has changed and not the position.
+ const bool new_pointer_shape = (frame_info.PointerShapeBufferSize != 0);
+ if (new_pointer_shape)
+ return false;
+
+ // The mouse cursor has moved and we can now query if the mouse pointer is
+ // drawn onto the desktop image or not to decide if we must draw the mouse
+ // pointer shape onto the desktop image (always done by default currently).
+ // Either the mouse pointer is already drawn onto the desktop image that
+ // IDXGIOutputDuplication::AcquireNextFrame provides or the mouse pointer is
+ // separate from the desktop image. If the mouse pointer is drawn onto the
+ // desktop image, the pointer position data that is reported by
+ // AcquireNextFrame indicates that a separate pointer isn’t visible, hence
+ // `frame_info.PointerPosition.Visible` is false.
+ const bool cursor_embedded_in_frame = !frame_info.PointerPosition.Visible;
+ RTC_HISTOGRAM_BOOLEAN("WebRTC.DesktopCapture.Win.DirectXCursorEmbedded",
+ cursor_embedded_in_frame);
+ return cursor_embedded_in_frame;
+}
+
+bool DxgiOutputDuplicator::Duplicate(Context* context,
+ DesktopVector offset,
+ SharedDesktopFrame* target) {
+ RTC_DCHECK(duplication_);
+ RTC_DCHECK(texture_);
+ RTC_DCHECK(target);
+ if (!DesktopRect::MakeSize(target->size())
+ .ContainsRect(GetTranslatedDesktopRect(offset))) {
+ // target size is not large enough to cover current output region.
+ return false;
+ }
+
+ DXGI_OUTDUPL_FRAME_INFO frame_info;
+ memset(&frame_info, 0, sizeof(frame_info));
+ ComPtr<IDXGIResource> resource;
+ _com_error error = duplication_->AcquireNextFrame(
+ kAcquireTimeoutMs, &frame_info, resource.GetAddressOf());
+ if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) {
+ RTC_LOG(LS_ERROR) << "Failed to capture frame: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ const bool cursor_embedded_in_frame = ContainsMouseCursor(frame_info);
+
+ // We need to merge updated region with the one from context, but only spread
+ // updated region from current frame. So keeps a copy of updated region from
+ // context here. The `updated_region` always starts from (0, 0).
+ DesktopRegion updated_region;
+ updated_region.Swap(&context->updated_region);
+ if (error.Error() == S_OK && frame_info.AccumulatedFrames > 0 && resource) {
+ DetectUpdatedRegion(frame_info, &context->updated_region);
+ SpreadContextChange(context);
+ if (!texture_->CopyFrom(frame_info, resource.Get())) {
+ return false;
+ }
+ updated_region.AddRegion(context->updated_region);
+ // TODO(zijiehe): Figure out why clearing context->updated_region() here
+ // triggers screen flickering?
+
+ const DesktopFrame& source = texture_->AsDesktopFrame();
+ if (rotation_ != Rotation::CLOCK_WISE_0) {
+ for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
+ it.Advance()) {
+ // The `updated_region` returned by Windows is rotated, but the `source`
+ // frame is not. So we need to rotate it reversely.
+ const DesktopRect source_rect =
+ RotateRect(it.rect(), desktop_size(), ReverseRotation(rotation_));
+ RotateDesktopFrame(source, source_rect, rotation_, offset, target);
+ }
+ } else {
+ for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
+ it.Advance()) {
+ // The DesktopRect in `target`, starts from offset.
+ DesktopRect dest_rect = it.rect();
+ dest_rect.Translate(offset);
+ target->CopyPixelsFrom(source, it.rect().top_left(), dest_rect);
+ }
+ }
+ last_frame_ = target->Share();
+ last_frame_offset_ = offset;
+ updated_region.Translate(offset.x(), offset.y());
+ target->mutable_updated_region()->AddRegion(updated_region);
+ target->set_may_contain_cursor(cursor_embedded_in_frame);
+ num_frames_captured_++;
+ return texture_->Release() && ReleaseFrame();
+ }
+
+ if (last_frame_) {
+ // No change since last frame or AcquireNextFrame() timed out, we will
+ // export last frame to the target.
+ for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
+ it.Advance()) {
+ // The DesktopRect in `source`, starts from last_frame_offset_.
+ DesktopRect source_rect = it.rect();
+ // The DesktopRect in `target`, starts from offset.
+ DesktopRect target_rect = source_rect;
+ source_rect.Translate(last_frame_offset_);
+ target_rect.Translate(offset);
+ target->CopyPixelsFrom(*last_frame_, source_rect.top_left(), target_rect);
+ }
+ updated_region.Translate(offset.x(), offset.y());
+ target->mutable_updated_region()->AddRegion(updated_region);
+ target->set_may_contain_cursor(cursor_embedded_in_frame);
+ } else {
+ // If we were at the very first frame, and capturing failed, the
+ // context->updated_region should be kept unchanged for next attempt.
+ context->updated_region.Swap(&updated_region);
+ }
+ // If AcquireNextFrame() failed with timeout error, we do not need to release
+ // the frame.
+ return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame();
+}
+
+DesktopRect DxgiOutputDuplicator::GetTranslatedDesktopRect(
+ DesktopVector offset) const {
+ DesktopRect result(DesktopRect::MakeSize(desktop_size()));
+ result.Translate(offset);
+ return result;
+}
+
+DesktopRect DxgiOutputDuplicator::GetUntranslatedDesktopRect() const {
+ return DesktopRect::MakeSize(desktop_size());
+}
+
+void DxgiOutputDuplicator::DetectUpdatedRegion(
+ const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ DesktopRegion* updated_region) {
+ if (DoDetectUpdatedRegion(frame_info, updated_region)) {
+ // Make sure even a region returned by Windows API is out of the scope of
+ // desktop_rect_, we still won't export it to the target DesktopFrame.
+ updated_region->IntersectWith(GetUntranslatedDesktopRect());
+ } else {
+ updated_region->SetRect(GetUntranslatedDesktopRect());
+ }
+}
+
+bool DxgiOutputDuplicator::DoDetectUpdatedRegion(
+ const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ DesktopRegion* updated_region) {
+ RTC_DCHECK(updated_region);
+ updated_region->Clear();
+ if (frame_info.TotalMetadataBufferSize == 0) {
+ // This should not happen, since frame_info.AccumulatedFrames > 0.
+ RTC_LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, "
+ << "but TotalMetadataBufferSize == 0";
+ return false;
+ }
+
+ if (metadata_.size() < frame_info.TotalMetadataBufferSize) {
+ metadata_.clear(); // Avoid data copy
+ metadata_.resize(frame_info.TotalMetadataBufferSize);
+ }
+
+ UINT buff_size = 0;
+ DXGI_OUTDUPL_MOVE_RECT* move_rects =
+ reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata_.data());
+ size_t move_rects_count = 0;
+ _com_error error = duplication_->GetFrameMoveRects(
+ static_cast<UINT>(metadata_.size()), move_rects, &buff_size);
+ if (error.Error() != S_OK) {
+ RTC_LOG(LS_ERROR) << "Failed to get move rectangles: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+ move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
+
+ RECT* dirty_rects = reinterpret_cast<RECT*>(metadata_.data() + buff_size);
+ size_t dirty_rects_count = 0;
+ error = duplication_->GetFrameDirtyRects(
+ static_cast<UINT>(metadata_.size()) - buff_size, dirty_rects, &buff_size);
+ if (error.Error() != S_OK) {
+ RTC_LOG(LS_ERROR) << "Failed to get dirty rectangles: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+ dirty_rects_count = buff_size / sizeof(RECT);
+
+ while (move_rects_count > 0) {
+ // DirectX capturer API may randomly return unmoved move_rects, which should
+ // be skipped to avoid unnecessary wasting of differing and encoding
+ // resources.
+ // By using testing application it2me_standalone_host_main, this check
+ // reduces average capture time by 0.375% (4.07 -> 4.055), and average
+ // encode time by 0.313% (8.042 -> 8.016) without other impacts.
+ if (move_rects->SourcePoint.x != move_rects->DestinationRect.left ||
+ move_rects->SourcePoint.y != move_rects->DestinationRect.top) {
+ updated_region->AddRect(
+ RotateRect(DesktopRect::MakeXYWH(move_rects->SourcePoint.x,
+ move_rects->SourcePoint.y,
+ move_rects->DestinationRect.right -
+ move_rects->DestinationRect.left,
+ move_rects->DestinationRect.bottom -
+ move_rects->DestinationRect.top),
+ unrotated_size_, rotation_));
+ updated_region->AddRect(
+ RotateRect(DesktopRect::MakeLTRB(move_rects->DestinationRect.left,
+ move_rects->DestinationRect.top,
+ move_rects->DestinationRect.right,
+ move_rects->DestinationRect.bottom),
+ unrotated_size_, rotation_));
+ } else {
+ RTC_LOG(LS_INFO) << "Unmoved move_rect detected, ["
+ << move_rects->DestinationRect.left << ", "
+ << move_rects->DestinationRect.top << "] - ["
+ << move_rects->DestinationRect.right << ", "
+ << move_rects->DestinationRect.bottom << "].";
+ }
+ move_rects++;
+ move_rects_count--;
+ }
+
+ while (dirty_rects_count > 0) {
+ updated_region->AddRect(RotateRect(
+ DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top,
+ dirty_rects->right, dirty_rects->bottom),
+ unrotated_size_, rotation_));
+ dirty_rects++;
+ dirty_rects_count--;
+ }
+
+ return true;
+}
+
+void DxgiOutputDuplicator::Setup(Context* context) {
+ RTC_DCHECK(context->updated_region.is_empty());
+ // Always copy entire monitor during the first Duplicate() function call.
+ context->updated_region.AddRect(GetUntranslatedDesktopRect());
+ RTC_DCHECK(std::find(contexts_.begin(), contexts_.end(), context) ==
+ contexts_.end());
+ contexts_.push_back(context);
+}
+
+void DxgiOutputDuplicator::Unregister(const Context* const context) {
+ auto it = std::find(contexts_.begin(), contexts_.end(), context);
+ RTC_DCHECK(it != contexts_.end());
+ contexts_.erase(it);
+}
+
+void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) {
+ for (Context* dest : contexts_) {
+ RTC_DCHECK(dest);
+ if (dest != source) {
+ dest->updated_region.AddRegion(source->updated_region);
+ }
+ }
+}
+
+DesktopSize DxgiOutputDuplicator::desktop_size() const {
+ return desktop_rect_.size();
+}
+
+int64_t DxgiOutputDuplicator::num_frames_captured() const {
+#if !defined(NDEBUG)
+ RTC_DCHECK_EQ(!!last_frame_, num_frames_captured_ > 0);
+#endif
+ return num_frames_captured_;
+}
+
+void DxgiOutputDuplicator::TranslateRect(const DesktopVector& position) {
+ desktop_rect_.Translate(position);
+ RTC_DCHECK_GE(desktop_rect_.left(), 0);
+ RTC_DCHECK_GE(desktop_rect_.top(), 0);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.h
new file mode 100644
index 0000000000..a4ce035d8b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.h
@@ -0,0 +1,154 @@
+/*
+ * 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 MODULES_DESKTOP_CAPTURE_WIN_DXGI_OUTPUT_DUPLICATOR_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_OUTPUT_DUPLICATOR_H_
+
+#include <comdef.h>
+#include <dxgi.h>
+#include <dxgi1_2.h>
+#include <wrl/client.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_frame_rotation.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/win/d3d_device.h"
+#include "modules/desktop_capture/win/dxgi_context.h"
+#include "modules/desktop_capture/win/dxgi_texture.h"
+#include "rtc_base/thread_annotations.h"
+
+namespace webrtc {
+
+// Duplicates the content on one IDXGIOutput, i.e. one monitor attached to one
+// video card. None of functions in this class is thread-safe.
+class DxgiOutputDuplicator {
+ public:
+ using Context = DxgiOutputContext;
+
+ // Creates an instance of DxgiOutputDuplicator from a D3dDevice and one of its
+ // IDXGIOutput1. Caller must maintain the lifetime of device, to make sure it
+ // outlives this instance. Only DxgiAdapterDuplicator can create an instance.
+ DxgiOutputDuplicator(const D3dDevice& device,
+ const Microsoft::WRL::ComPtr<IDXGIOutput1>& output,
+ const DXGI_OUTPUT_DESC& desc);
+
+ // To allow this class to work with vector.
+ DxgiOutputDuplicator(DxgiOutputDuplicator&& other);
+
+ // Destructs this instance. We need to make sure texture_ has been released
+ // before duplication_.
+ ~DxgiOutputDuplicator();
+
+ // Initializes duplication_ object.
+ bool Initialize();
+
+ // Copies the content of current IDXGIOutput to the `target`. To improve the
+ // performance, this function copies only regions merged from
+ // `context`->updated_region and DetectUpdatedRegion(). The `offset` decides
+ // the offset in the `target` where the content should be copied to. i.e. this
+ // function copies the content to the rectangle of (offset.x(), offset.y()) to
+ // (offset.x() + desktop_rect_.width(), offset.y() + desktop_rect_.height()).
+ // Returns false in case of a failure.
+ // May retain a reference to `target` so that a "captured" frame can be
+ // returned in the event that a new frame is not ready to be captured yet.
+ // (Or in other words, if the call to IDXGIOutputDuplication::AcquireNextFrame
+ // indicates that there is not yet a new frame, this is usually because no
+ // updates have occurred to the frame).
+ bool Duplicate(Context* context,
+ DesktopVector offset,
+ SharedDesktopFrame* target);
+
+ // Returns the desktop rect covered by this DxgiOutputDuplicator.
+ DesktopRect desktop_rect() const { return desktop_rect_; }
+
+ // Returns the device name from DXGI_OUTPUT_DESC in utf8 encoding.
+ const std::string& device_name() const { return device_name_; }
+
+ void Setup(Context* context);
+
+ void Unregister(const Context* const context);
+
+ // How many frames have been captured by this DxigOutputDuplicator.
+ int64_t num_frames_captured() const;
+
+ // Moves `desktop_rect_`. See DxgiDuplicatorController::TranslateRect().
+ void TranslateRect(const DesktopVector& position);
+
+ private:
+ // Calls DoDetectUpdatedRegion(). If it fails, this function sets the
+ // `updated_region` as entire UntranslatedDesktopRect().
+ void DetectUpdatedRegion(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ DesktopRegion* updated_region);
+
+ // Returns untranslated updated region, which are directly returned by Windows
+ // APIs. Returns false in case of a failure.
+ bool DoDetectUpdatedRegion(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ DesktopRegion* updated_region);
+
+ // Returns true if the mouse cursor is embedded in the captured frame and
+ // false if not. Also logs the same boolean as
+ // WebRTC.DesktopCapture.Win.DirectXCursorEmbedded UMA.
+ bool ContainsMouseCursor(const DXGI_OUTDUPL_FRAME_INFO& frame_info);
+
+ bool ReleaseFrame();
+
+ // Initializes duplication_ instance. Expects duplication_ is in empty status.
+ // Returns false if system does not support IDXGIOutputDuplication.
+ bool DuplicateOutput();
+
+ // Returns a DesktopRect with the same size of desktop_size(), but translated
+ // by offset.
+ DesktopRect GetTranslatedDesktopRect(DesktopVector offset) const;
+
+ // Returns a DesktopRect with the same size of desktop_size(), but starts from
+ // (0, 0).
+ DesktopRect GetUntranslatedDesktopRect() const;
+
+ // Spreads changes from `context` to other registered Context(s) in
+ // contexts_.
+ void SpreadContextChange(const Context* const context);
+
+ // Returns the size of desktop rectangle current instance representing.
+ DesktopSize desktop_size() const;
+
+ const D3dDevice device_;
+ const Microsoft::WRL::ComPtr<IDXGIOutput1> output_;
+ const std::string device_name_;
+ DesktopRect desktop_rect_;
+ Microsoft::WRL::ComPtr<IDXGIOutputDuplication> duplication_;
+ DXGI_OUTDUPL_DESC desc_;
+ std::vector<uint8_t> metadata_;
+ std::unique_ptr<DxgiTexture> texture_;
+ Rotation rotation_;
+ DesktopSize unrotated_size_;
+
+ // After each AcquireNextFrame() function call, updated_region_(s) of all
+ // active Context(s) need to be updated. Since they have missed the
+ // change this time. And during next Duplicate() function call, their
+ // updated_region_ will be merged and copied.
+ std::vector<Context*> contexts_;
+
+ // The last full frame of this output and its offset. If on AcquireNextFrame()
+ // failed because of timeout, i.e. no update, we can copy content from
+ // `last_frame_`.
+ std::unique_ptr<SharedDesktopFrame> last_frame_;
+ DesktopVector last_frame_offset_;
+
+ int64_t num_frames_captured_ = 0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_OUTPUT_DUPLICATOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.cc
new file mode 100644
index 0000000000..b8f5b81f90
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.cc
@@ -0,0 +1,81 @@
+/*
+ * 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 "modules/desktop_capture/win/dxgi_texture.h"
+
+#include <comdef.h>
+#include <d3d11.h>
+#include <wrl/client.h>
+
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+using Microsoft::WRL::ComPtr;
+
+namespace webrtc {
+
+namespace {
+
+class DxgiDesktopFrame : public DesktopFrame {
+ public:
+ explicit DxgiDesktopFrame(const DxgiTexture& texture)
+ : DesktopFrame(texture.desktop_size(),
+ texture.pitch(),
+ texture.bits(),
+ nullptr) {}
+
+ ~DxgiDesktopFrame() override = default;
+};
+
+} // namespace
+
+DxgiTexture::DxgiTexture() = default;
+DxgiTexture::~DxgiTexture() = default;
+
+bool DxgiTexture::CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ IDXGIResource* resource) {
+ RTC_DCHECK_GT(frame_info.AccumulatedFrames, 0);
+ RTC_DCHECK(resource);
+ ComPtr<ID3D11Texture2D> texture;
+ _com_error error = resource->QueryInterface(
+ __uuidof(ID3D11Texture2D),
+ reinterpret_cast<void**>(texture.GetAddressOf()));
+ if (error.Error() != S_OK || !texture) {
+ RTC_LOG(LS_ERROR) << "Failed to convert IDXGIResource to ID3D11Texture2D: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ D3D11_TEXTURE2D_DESC desc = {0};
+ texture->GetDesc(&desc);
+ desktop_size_.set(desc.Width, desc.Height);
+
+ return CopyFromTexture(frame_info, texture.Get());
+}
+
+const DesktopFrame& DxgiTexture::AsDesktopFrame() {
+ if (!frame_) {
+ frame_.reset(new DxgiDesktopFrame(*this));
+ }
+ return *frame_;
+}
+
+bool DxgiTexture::Release() {
+ frame_.reset();
+ return DoRelease();
+}
+
+DXGI_MAPPED_RECT* DxgiTexture::rect() {
+ return &rect_;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.h
new file mode 100644
index 0000000000..a663b95a04
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.h
@@ -0,0 +1,73 @@
+/*
+ * 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 MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_H_
+
+#include <d3d11.h>
+#include <dxgi1_2.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+class DesktopRegion;
+
+// A texture copied or mapped from a DXGI_OUTDUPL_FRAME_INFO and IDXGIResource.
+class DxgiTexture {
+ public:
+ // Creates a DxgiTexture instance, which represents the `desktop_size` area of
+ // entire screen -- usually a monitor on the system.
+ DxgiTexture();
+
+ virtual ~DxgiTexture();
+
+ // Copies selected regions of a frame represented by frame_info and resource.
+ // Returns false if anything wrong.
+ bool CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ IDXGIResource* resource);
+
+ const DesktopSize& desktop_size() const { return desktop_size_; }
+
+ uint8_t* bits() const { return static_cast<uint8_t*>(rect_.pBits); }
+
+ int pitch() const { return static_cast<int>(rect_.Pitch); }
+
+ // Releases the resource currently holds by this instance. Returns false if
+ // anything wrong, and this instance should be deprecated in this state. bits,
+ // pitch and AsDesktopFrame are only valid after a success CopyFrom() call,
+ // but before Release() call.
+ bool Release();
+
+ // Returns a DesktopFrame snapshot of a DxgiTexture instance. This
+ // DesktopFrame is used to copy a DxgiTexture content to another DesktopFrame
+ // only. And it should not outlive its DxgiTexture instance.
+ const DesktopFrame& AsDesktopFrame();
+
+ protected:
+ DXGI_MAPPED_RECT* rect();
+
+ virtual bool CopyFromTexture(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ ID3D11Texture2D* texture) = 0;
+
+ virtual bool DoRelease() = 0;
+
+ private:
+ DXGI_MAPPED_RECT rect_ = {0};
+ DesktopSize desktop_size_;
+ std::unique_ptr<DesktopFrame> frame_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc
new file mode 100644
index 0000000000..7ecf1adc61
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc
@@ -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.
+ */
+
+#include "modules/desktop_capture/win/dxgi_texture_mapping.h"
+
+#include <comdef.h>
+#include <dxgi.h>
+#include <dxgi1_2.h>
+
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+DxgiTextureMapping::DxgiTextureMapping(IDXGIOutputDuplication* duplication)
+ : duplication_(duplication) {
+ RTC_DCHECK(duplication_);
+}
+
+DxgiTextureMapping::~DxgiTextureMapping() = default;
+
+bool DxgiTextureMapping::CopyFromTexture(
+ const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ ID3D11Texture2D* texture) {
+ RTC_DCHECK_GT(frame_info.AccumulatedFrames, 0);
+ RTC_DCHECK(texture);
+ *rect() = {0};
+ _com_error error = duplication_->MapDesktopSurface(rect());
+ if (error.Error() != S_OK) {
+ *rect() = {0};
+ RTC_LOG(LS_ERROR)
+ << "Failed to map the IDXGIOutputDuplication to a bitmap: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ return true;
+}
+
+bool DxgiTextureMapping::DoRelease() {
+ _com_error error = duplication_->UnMapDesktopSurface();
+ if (error.Error() != S_OK) {
+ RTC_LOG(LS_ERROR) << "Failed to unmap the IDXGIOutputDuplication: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.h
new file mode 100644
index 0000000000..71f00b99ab
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.h
@@ -0,0 +1,47 @@
+/*
+ * 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 MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_MAPPING_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_MAPPING_H_
+
+#include <d3d11.h>
+#include <dxgi1_2.h>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/win/dxgi_texture.h"
+
+namespace webrtc {
+
+// A DxgiTexture which directly maps bitmap from IDXGIResource. This class is
+// used when DXGI_OUTDUPL_DESC.DesktopImageInSystemMemory is true. (This usually
+// means the video card shares main memory with CPU, instead of having its own
+// individual memory.)
+class DxgiTextureMapping : public DxgiTexture {
+ public:
+ // Creates a DxgiTextureMapping instance. Caller must maintain the lifetime
+ // of input `duplication` to make sure it outlives this instance.
+ explicit DxgiTextureMapping(IDXGIOutputDuplication* duplication);
+
+ ~DxgiTextureMapping() override;
+
+ protected:
+ bool CopyFromTexture(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ ID3D11Texture2D* texture) override;
+
+ bool DoRelease() override;
+
+ private:
+ IDXGIOutputDuplication* const duplication_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_MAPPING_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.cc
new file mode 100644
index 0000000000..17e8518a7d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.cc
@@ -0,0 +1,132 @@
+/*
+ * 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 "modules/desktop_capture/win/dxgi_texture_staging.h"
+
+#include <comdef.h>
+#include <dxgi.h>
+#include <dxgi1_2.h>
+#include <unknwn.h>
+
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "system_wrappers/include/metrics.h"
+
+using Microsoft::WRL::ComPtr;
+
+namespace webrtc {
+
+DxgiTextureStaging::DxgiTextureStaging(const D3dDevice& device)
+ : device_(device) {}
+
+DxgiTextureStaging::~DxgiTextureStaging() = default;
+
+bool DxgiTextureStaging::InitializeStage(ID3D11Texture2D* texture) {
+ RTC_DCHECK(texture);
+ D3D11_TEXTURE2D_DESC desc = {0};
+ texture->GetDesc(&desc);
+
+ desc.ArraySize = 1;
+ desc.BindFlags = 0;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.MipLevels = 1;
+ desc.MiscFlags = 0;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.Usage = D3D11_USAGE_STAGING;
+ if (stage_) {
+ AssertStageAndSurfaceAreSameObject();
+ D3D11_TEXTURE2D_DESC current_desc;
+ stage_->GetDesc(&current_desc);
+ const bool recreate_needed =
+ (memcmp(&desc, &current_desc, sizeof(D3D11_TEXTURE2D_DESC)) != 0);
+ RTC_HISTOGRAM_BOOLEAN("WebRTC.DesktopCapture.StagingTextureRecreate",
+ recreate_needed);
+ if (!recreate_needed) {
+ return true;
+ }
+
+ // The descriptions are not consistent, we need to create a new
+ // ID3D11Texture2D instance.
+ stage_.Reset();
+ surface_.Reset();
+ } else {
+ RTC_DCHECK(!surface_);
+ }
+
+ _com_error error = device_.d3d_device()->CreateTexture2D(
+ &desc, nullptr, stage_.GetAddressOf());
+ if (error.Error() != S_OK || !stage_) {
+ RTC_LOG(LS_ERROR) << "Failed to create a new ID3D11Texture2D as stage: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ error = stage_.As(&surface_);
+ if (error.Error() != S_OK || !surface_) {
+ RTC_LOG(LS_ERROR) << "Failed to convert ID3D11Texture2D to IDXGISurface: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ return true;
+}
+
+void DxgiTextureStaging::AssertStageAndSurfaceAreSameObject() {
+ ComPtr<IUnknown> left;
+ ComPtr<IUnknown> right;
+ bool left_result = SUCCEEDED(stage_.As(&left));
+ bool right_result = SUCCEEDED(surface_.As(&right));
+ RTC_DCHECK(left_result);
+ RTC_DCHECK(right_result);
+ RTC_DCHECK(left.Get() == right.Get());
+}
+
+bool DxgiTextureStaging::CopyFromTexture(
+ const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ ID3D11Texture2D* texture) {
+ RTC_DCHECK_GT(frame_info.AccumulatedFrames, 0);
+ RTC_DCHECK(texture);
+
+ // AcquireNextFrame returns a CPU inaccessible IDXGIResource, so we need to
+ // copy it to a CPU accessible staging ID3D11Texture2D.
+ if (!InitializeStage(texture)) {
+ return false;
+ }
+
+ device_.context()->CopyResource(static_cast<ID3D11Resource*>(stage_.Get()),
+ static_cast<ID3D11Resource*>(texture));
+
+ *rect() = {0};
+ _com_error error = surface_->Map(rect(), DXGI_MAP_READ);
+ if (error.Error() != S_OK) {
+ *rect() = {0};
+ RTC_LOG(LS_ERROR) << "Failed to map the IDXGISurface to a bitmap: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ return true;
+}
+
+bool DxgiTextureStaging::DoRelease() {
+ _com_error error = surface_->Unmap();
+ if (error.Error() != S_OK) {
+ stage_.Reset();
+ surface_.Reset();
+ }
+ // If using staging mode, we only need to recreate ID3D11Texture2D instance.
+ // This will happen during next CopyFrom call. So this function always returns
+ // true.
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.h
new file mode 100644
index 0000000000..e8c2af6662
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.h
@@ -0,0 +1,68 @@
+/*
+ * 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 MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_STAGING_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_STAGING_H_
+
+#include <d3d11.h>
+#include <dxgi1_2.h>
+#include <wrl/client.h>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/win/d3d_device.h"
+#include "modules/desktop_capture/win/dxgi_texture.h"
+
+namespace webrtc {
+
+// A pair of an ID3D11Texture2D and an IDXGISurface. We need an ID3D11Texture2D
+// instance to copy GPU texture to RAM, but an IDXGISurface instance to map the
+// texture into a bitmap buffer. These two instances are pointing to a same
+// object.
+//
+// An ID3D11Texture2D is created by an ID3D11Device, so a DxgiTexture cannot be
+// shared between two DxgiAdapterDuplicators.
+class DxgiTextureStaging : public DxgiTexture {
+ public:
+ // Creates a DxgiTextureStaging instance. Caller must maintain the lifetime
+ // of input device to make sure it outlives this instance.
+ explicit DxgiTextureStaging(const D3dDevice& device);
+
+ ~DxgiTextureStaging() override;
+
+ protected:
+ // Copies selected regions of a frame represented by frame_info and texture.
+ // Returns false if anything wrong.
+ bool CopyFromTexture(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ ID3D11Texture2D* texture) override;
+
+ bool DoRelease() override;
+
+ private:
+ // Initializes stage_ from a CPU inaccessible IDXGIResource. Returns false if
+ // it failed to execute Windows APIs, or the size of the texture is not
+ // consistent with desktop_rect.
+ bool InitializeStage(ID3D11Texture2D* texture);
+
+ // Makes sure stage_ and surface_ are always pointing to a same object.
+ // We need an ID3D11Texture2D instance for
+ // ID3D11DeviceContext::CopySubresourceRegion, but an IDXGISurface for
+ // IDXGISurface::Map.
+ void AssertStageAndSurfaceAreSameObject();
+
+ const DesktopRect desktop_rect_;
+ const D3dDevice device_;
+ Microsoft::WRL::ComPtr<ID3D11Texture2D> stage_;
+ Microsoft::WRL::ComPtr<IDXGISurface> surface_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_STAGING_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc b/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc
new file mode 100644
index 0000000000..4222dfc01e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2019 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 "modules/desktop_capture/win/full_screen_win_application_handler.h"
+
+#include <algorithm>
+#include <cwctype>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/strings/ascii.h"
+#include "absl/strings/match.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+#include "rtc_base/arraysize.h"
+#include "rtc_base/logging.h" // For RTC_LOG_GLE
+#include "rtc_base/string_utils.h"
+
+namespace webrtc {
+namespace {
+
+// Utility function to verify that `window` has class name equal to `class_name`
+bool CheckWindowClassName(HWND window, const wchar_t* class_name) {
+ const size_t classNameLength = wcslen(class_name);
+
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassa
+ // says lpszClassName field in WNDCLASS is limited by 256 symbols, so we don't
+ // need to have a buffer bigger than that.
+ constexpr size_t kMaxClassNameLength = 256;
+ WCHAR buffer[kMaxClassNameLength];
+
+ const int length = ::GetClassNameW(window, buffer, kMaxClassNameLength);
+ if (length <= 0)
+ return false;
+
+ if (static_cast<size_t>(length) != classNameLength)
+ return false;
+ return wcsncmp(buffer, class_name, classNameLength) == 0;
+}
+
+std::string WindowText(HWND window) {
+ size_t len = ::GetWindowTextLength(window);
+ if (len == 0)
+ return std::string();
+
+ std::vector<wchar_t> buffer(len + 1, 0);
+ size_t copied = ::GetWindowTextW(window, buffer.data(), buffer.size());
+ if (copied == 0)
+ return std::string();
+ return rtc::ToUtf8(buffer.data(), copied);
+}
+
+DWORD WindowProcessId(HWND window) {
+ DWORD dwProcessId = 0;
+ ::GetWindowThreadProcessId(window, &dwProcessId);
+ return dwProcessId;
+}
+
+std::wstring FileNameFromPath(const std::wstring& path) {
+ auto found = path.rfind(L"\\");
+ if (found == std::string::npos)
+ return path;
+ return path.substr(found + 1);
+}
+
+// Returns windows which belong to given process id
+// `sources` is a full list of available windows
+// `processId` is a process identifier (window owner)
+// `window_to_exclude` is a window to be exluded from result
+DesktopCapturer::SourceList GetProcessWindows(
+ const DesktopCapturer::SourceList& sources,
+ DWORD processId,
+ HWND window_to_exclude) {
+ DesktopCapturer::SourceList result;
+ std::copy_if(sources.begin(), sources.end(), std::back_inserter(result),
+ [&](DesktopCapturer::Source source) {
+ const HWND source_hwnd = reinterpret_cast<HWND>(source.id);
+ return window_to_exclude != source_hwnd &&
+ WindowProcessId(source_hwnd) == processId;
+ });
+ return result;
+}
+
+class FullScreenPowerPointHandler : public FullScreenApplicationHandler {
+ public:
+ explicit FullScreenPowerPointHandler(DesktopCapturer::SourceId sourceId)
+ : FullScreenApplicationHandler(sourceId) {}
+
+ ~FullScreenPowerPointHandler() override {}
+
+ DesktopCapturer::SourceId FindFullScreenWindow(
+ const DesktopCapturer::SourceList& window_list,
+ int64_t timestamp) const override {
+ if (window_list.empty())
+ return 0;
+
+ HWND original_window = reinterpret_cast<HWND>(GetSourceId());
+ DWORD process_id = WindowProcessId(original_window);
+
+ DesktopCapturer::SourceList powerpoint_windows =
+ GetProcessWindows(window_list, process_id, original_window);
+
+ if (powerpoint_windows.empty())
+ return 0;
+
+ if (GetWindowType(original_window) != WindowType::kEditor)
+ return 0;
+
+ const auto original_document = GetDocumentFromEditorTitle(original_window);
+
+ for (const auto& source : powerpoint_windows) {
+ HWND window = reinterpret_cast<HWND>(source.id);
+
+ // Looking for slide show window for the same document
+ if (GetWindowType(window) != WindowType::kSlideShow ||
+ GetDocumentFromSlideShowTitle(window) != original_document) {
+ continue;
+ }
+
+ return source.id;
+ }
+
+ return 0;
+ }
+
+ private:
+ enum class WindowType { kEditor, kSlideShow, kOther };
+
+ WindowType GetWindowType(HWND window) const {
+ if (IsEditorWindow(window))
+ return WindowType::kEditor;
+ else if (IsSlideShowWindow(window))
+ return WindowType::kSlideShow;
+ else
+ return WindowType::kOther;
+ }
+
+ constexpr static char kDocumentTitleSeparator[] = " - ";
+
+ std::string GetDocumentFromEditorTitle(HWND window) const {
+ std::string title = WindowText(window);
+ auto position = title.find(kDocumentTitleSeparator);
+ return std::string(absl::StripAsciiWhitespace(
+ absl::string_view(title).substr(0, position)));
+ }
+
+ std::string GetDocumentFromSlideShowTitle(HWND window) const {
+ std::string title = WindowText(window);
+ auto left_pos = title.find(kDocumentTitleSeparator);
+ auto right_pos = title.rfind(kDocumentTitleSeparator);
+ constexpr size_t kSeparatorLength = arraysize(kDocumentTitleSeparator) - 1;
+ if (left_pos == std::string::npos || right_pos == std::string::npos)
+ return title;
+
+ if (right_pos > left_pos + kSeparatorLength) {
+ auto result_len = right_pos - left_pos - kSeparatorLength;
+ auto document = absl::string_view(title).substr(
+ left_pos + kSeparatorLength, result_len);
+ return std::string(absl::StripAsciiWhitespace(document));
+ } else {
+ auto document = absl::string_view(title).substr(
+ left_pos + kSeparatorLength, std::wstring::npos);
+ return std::string(absl::StripAsciiWhitespace(document));
+ }
+ }
+
+ bool IsEditorWindow(HWND window) const {
+ return CheckWindowClassName(window, L"PPTFrameClass");
+ }
+
+ bool IsSlideShowWindow(HWND window) const {
+ const LONG style = ::GetWindowLong(window, GWL_STYLE);
+ const bool min_box = WS_MINIMIZEBOX & style;
+ const bool max_box = WS_MAXIMIZEBOX & style;
+ return !min_box && !max_box;
+ }
+};
+
+class OpenOfficeApplicationHandler : public FullScreenApplicationHandler {
+ public:
+ explicit OpenOfficeApplicationHandler(DesktopCapturer::SourceId sourceId)
+ : FullScreenApplicationHandler(sourceId) {}
+
+ DesktopCapturer::SourceId FindFullScreenWindow(
+ const DesktopCapturer::SourceList& window_list,
+ int64_t timestamp) const override {
+ if (window_list.empty())
+ return 0;
+
+ DWORD process_id = WindowProcessId(reinterpret_cast<HWND>(GetSourceId()));
+
+ DesktopCapturer::SourceList app_windows =
+ GetProcessWindows(window_list, process_id, nullptr);
+
+ DesktopCapturer::SourceList document_windows;
+ std::copy_if(
+ app_windows.begin(), app_windows.end(),
+ std::back_inserter(document_windows),
+ [this](const DesktopCapturer::Source& x) { return IsEditorWindow(x); });
+
+ // Check if we have only one document window, otherwise it's not possible
+ // to securely match a document window and a slide show window which has
+ // empty title.
+ if (document_windows.size() != 1) {
+ return 0;
+ }
+
+ // Check if document window has been selected as a source
+ if (document_windows.front().id != GetSourceId()) {
+ return 0;
+ }
+
+ // Check if we have a slide show window.
+ auto slide_show_window =
+ std::find_if(app_windows.begin(), app_windows.end(),
+ [this](const DesktopCapturer::Source& x) {
+ return IsSlideShowWindow(x);
+ });
+
+ if (slide_show_window == app_windows.end())
+ return 0;
+
+ return slide_show_window->id;
+ }
+
+ private:
+ bool IsEditorWindow(const DesktopCapturer::Source& source) const {
+ if (source.title.empty()) {
+ return false;
+ }
+
+ return CheckWindowClassName(reinterpret_cast<HWND>(source.id), L"SALFRAME");
+ }
+
+ bool IsSlideShowWindow(const DesktopCapturer::Source& source) const {
+ // Check title size to filter out a Presenter Control window which shares
+ // window class with Slide Show window but has non empty title.
+ if (!source.title.empty()) {
+ return false;
+ }
+
+ return CheckWindowClassName(reinterpret_cast<HWND>(source.id),
+ L"SALTMPSUBFRAME");
+ }
+};
+
+std::wstring GetPathByWindowId(HWND window_id) {
+ DWORD process_id = WindowProcessId(window_id);
+ HANDLE process =
+ ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id);
+ if (process == NULL)
+ return L"";
+ DWORD path_len = MAX_PATH;
+ WCHAR path[MAX_PATH];
+ std::wstring result;
+ if (::QueryFullProcessImageNameW(process, 0, path, &path_len))
+ result = std::wstring(path, path_len);
+ else
+ RTC_LOG_GLE(LS_ERROR) << "QueryFullProcessImageName failed.";
+
+ ::CloseHandle(process);
+ return result;
+}
+
+} // namespace
+
+std::unique_ptr<FullScreenApplicationHandler>
+CreateFullScreenWinApplicationHandler(DesktopCapturer::SourceId source_id) {
+ std::unique_ptr<FullScreenApplicationHandler> result;
+ HWND hwnd = reinterpret_cast<HWND>(source_id);
+ std::wstring exe_path = GetPathByWindowId(hwnd);
+ std::wstring file_name = FileNameFromPath(exe_path);
+ std::transform(file_name.begin(), file_name.end(), file_name.begin(),
+ std::towupper);
+
+ if (file_name == L"POWERPNT.EXE") {
+ result = std::make_unique<FullScreenPowerPointHandler>(source_id);
+ } else if (file_name == L"SOFFICE.BIN" &&
+ absl::EndsWith(WindowText(hwnd), "OpenOffice Impress")) {
+ result = std::make_unique<OpenOfficeApplicationHandler>(source_id);
+ }
+
+ return result;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.h b/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.h
new file mode 100644
index 0000000000..286e8e5cb9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2019 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 MODULES_DESKTOP_CAPTURE_WIN_FULL_SCREEN_WIN_APPLICATION_HANDLER_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_FULL_SCREEN_WIN_APPLICATION_HANDLER_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/full_screen_application_handler.h"
+
+namespace webrtc {
+
+std::unique_ptr<FullScreenApplicationHandler>
+CreateFullScreenWinApplicationHandler(DesktopCapturer::SourceId sourceId);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_FULL_SCREEN_WIN_APPLICATION_HANDLER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/scoped_gdi_object.h b/third_party/libwebrtc/modules/desktop_capture/win/scoped_gdi_object.h
new file mode 100644
index 0000000000..2b01941e20
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/scoped_gdi_object.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_
+
+#include <windows.h>
+
+namespace webrtc {
+namespace win {
+
+// Scoper for GDI objects.
+template <class T, class Traits>
+class ScopedGDIObject {
+ public:
+ ScopedGDIObject() : handle_(NULL) {}
+ explicit ScopedGDIObject(T object) : handle_(object) {}
+
+ ~ScopedGDIObject() { Traits::Close(handle_); }
+
+ ScopedGDIObject(const ScopedGDIObject&) = delete;
+ ScopedGDIObject& operator=(const ScopedGDIObject&) = delete;
+
+ T Get() { return handle_; }
+
+ void Set(T object) {
+ if (handle_ && object != handle_)
+ Traits::Close(handle_);
+ handle_ = object;
+ }
+
+ ScopedGDIObject& operator=(T object) {
+ Set(object);
+ return *this;
+ }
+
+ T release() {
+ T object = handle_;
+ handle_ = NULL;
+ return object;
+ }
+
+ operator T() { return handle_; }
+
+ private:
+ T handle_;
+};
+
+// The traits class that uses DeleteObject() to close a handle.
+template <typename T>
+class DeleteObjectTraits {
+ public:
+ DeleteObjectTraits() = delete;
+ DeleteObjectTraits(const DeleteObjectTraits&) = delete;
+ DeleteObjectTraits& operator=(const DeleteObjectTraits&) = delete;
+
+ // Closes the handle.
+ static void Close(T handle) {
+ if (handle)
+ DeleteObject(handle);
+ }
+};
+
+// The traits class that uses DestroyCursor() to close a handle.
+class DestroyCursorTraits {
+ public:
+ DestroyCursorTraits() = delete;
+ DestroyCursorTraits(const DestroyCursorTraits&) = delete;
+ DestroyCursorTraits& operator=(const DestroyCursorTraits&) = delete;
+
+ // Closes the handle.
+ static void Close(HCURSOR handle) {
+ if (handle)
+ DestroyCursor(handle);
+ }
+};
+
+typedef ScopedGDIObject<HBITMAP, DeleteObjectTraits<HBITMAP> > ScopedBitmap;
+typedef ScopedGDIObject<HCURSOR, DestroyCursorTraits> ScopedCursor;
+
+} // namespace win
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.cc b/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.cc
new file mode 100644
index 0000000000..22e8e7bc8f
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.cc
@@ -0,0 +1,54 @@
+/*
+ * 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 "modules/desktop_capture/win/scoped_thread_desktop.h"
+
+#include "modules/desktop_capture/win/desktop.h"
+
+namespace webrtc {
+
+ScopedThreadDesktop::ScopedThreadDesktop()
+ : initial_(Desktop::GetThreadDesktop()) {}
+
+ScopedThreadDesktop::~ScopedThreadDesktop() {
+ Revert();
+}
+
+bool ScopedThreadDesktop::IsSame(const Desktop& desktop) {
+ if (assigned_.get() != NULL) {
+ return assigned_->IsSame(desktop);
+ } else {
+ return initial_->IsSame(desktop);
+ }
+}
+
+void ScopedThreadDesktop::Revert() {
+ if (assigned_.get() != NULL) {
+ initial_->SetThreadDesktop();
+ assigned_.reset();
+ }
+}
+
+bool ScopedThreadDesktop::SetThreadDesktop(Desktop* desktop) {
+ Revert();
+
+ std::unique_ptr<Desktop> scoped_desktop(desktop);
+
+ if (initial_->IsSame(*desktop))
+ return true;
+
+ if (!desktop->SetThreadDesktop())
+ return false;
+
+ assigned_.reset(scoped_desktop.release());
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.h b/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.h
new file mode 100644
index 0000000000..98f151a46c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_SCOPED_THREAD_DESKTOP_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCOPED_THREAD_DESKTOP_H_
+
+#include <windows.h>
+
+#include <memory>
+
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+class Desktop;
+
+class RTC_EXPORT ScopedThreadDesktop {
+ public:
+ ScopedThreadDesktop();
+ ~ScopedThreadDesktop();
+
+ ScopedThreadDesktop(const ScopedThreadDesktop&) = delete;
+ ScopedThreadDesktop& operator=(const ScopedThreadDesktop&) = delete;
+
+ // Returns true if `desktop` has the same desktop name as the currently
+ // assigned desktop (if assigned) or as the initial desktop (if not assigned).
+ // Returns false in any other case including failing Win32 APIs and
+ // uninitialized desktop handles.
+ bool IsSame(const Desktop& desktop);
+
+ // Reverts the calling thread to use the initial desktop.
+ void Revert();
+
+ // Assigns `desktop` to be the calling thread. Returns true if the thread has
+ // been switched to `desktop` successfully. Takes ownership of `desktop`.
+ bool SetThreadDesktop(Desktop* desktop);
+
+ private:
+ // The desktop handle assigned to the calling thread by Set
+ std::unique_ptr<Desktop> assigned_;
+
+ // The desktop handle assigned to the calling thread at creation.
+ std::unique_ptr<Desktop> initial_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCOPED_THREAD_DESKTOP_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.cc
new file mode 100644
index 0000000000..f68cfb94c1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.cc
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2014 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 "modules/desktop_capture/win/screen_capture_utils.h"
+
+#include <shellscalingapi.h>
+#include <windows.h>
+
+#include <string>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/string_utils.h"
+#include "rtc_base/win32.h"
+
+namespace webrtc {
+
+bool HasActiveDisplay() {
+ DesktopCapturer::SourceList screens;
+
+ return GetScreenList(&screens) && !screens.empty();
+}
+
+bool GetScreenList(DesktopCapturer::SourceList* screens,
+ std::vector<std::string>* device_names /* = nullptr */) {
+ RTC_DCHECK(screens->empty());
+ RTC_DCHECK(!device_names || device_names->empty());
+
+ BOOL enum_result = TRUE;
+ for (int device_index = 0;; ++device_index) {
+ DISPLAY_DEVICEW device;
+ device.cb = sizeof(device);
+ enum_result = EnumDisplayDevicesW(NULL, device_index, &device, 0);
+
+ // `enum_result` is 0 if we have enumerated all devices.
+ if (!enum_result) {
+ break;
+ }
+
+ // We only care about active displays.
+ if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE)) {
+ continue;
+ }
+
+ screens->push_back({device_index, 0, std::string()});
+ if (device_names) {
+ device_names->push_back(rtc::ToUtf8(device.DeviceName));
+ }
+ }
+ return true;
+}
+
+bool GetHmonitorFromDeviceIndex(const DesktopCapturer::SourceId device_index,
+ HMONITOR* hmonitor) {
+ // A device index of `kFullDesktopScreenId` or -1 represents all screens, an
+ // HMONITOR of 0 indicates the same.
+ if (device_index == kFullDesktopScreenId) {
+ *hmonitor = 0;
+ return true;
+ }
+
+ std::wstring device_key;
+ if (!IsScreenValid(device_index, &device_key)) {
+ return false;
+ }
+
+ DesktopRect screen_rect = GetScreenRect(device_index, device_key);
+ if (screen_rect.is_empty()) {
+ return false;
+ }
+
+ RECT rect = {screen_rect.left(), screen_rect.top(), screen_rect.right(),
+ screen_rect.bottom()};
+
+ HMONITOR monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
+ if (monitor == NULL) {
+ RTC_LOG(LS_WARNING) << "No HMONITOR found for supplied device index.";
+ return false;
+ }
+
+ *hmonitor = monitor;
+ return true;
+}
+
+bool IsMonitorValid(const HMONITOR monitor) {
+ // An HMONITOR of 0 refers to a virtual monitor that spans all physical
+ // monitors.
+ if (monitor == 0) {
+ // There is a bug in a Windows OS API that causes a crash when capturing if
+ // there are no active displays. We must ensure there is an active display
+ // before returning true.
+ if (!HasActiveDisplay())
+ return false;
+
+ return true;
+ }
+
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(MONITORINFO);
+ return GetMonitorInfoA(monitor, &monitor_info);
+}
+
+DesktopRect GetMonitorRect(const HMONITOR monitor) {
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(MONITORINFO);
+ if (!GetMonitorInfoA(monitor, &monitor_info)) {
+ return DesktopRect();
+ }
+
+ return DesktopRect::MakeLTRB(
+ monitor_info.rcMonitor.left, monitor_info.rcMonitor.top,
+ monitor_info.rcMonitor.right, monitor_info.rcMonitor.bottom);
+}
+
+bool IsScreenValid(const DesktopCapturer::SourceId screen,
+ std::wstring* device_key) {
+ if (screen == kFullDesktopScreenId) {
+ *device_key = L"";
+ return true;
+ }
+
+ DISPLAY_DEVICEW device;
+ device.cb = sizeof(device);
+ BOOL enum_result = EnumDisplayDevicesW(NULL, screen, &device, 0);
+ if (enum_result) {
+ *device_key = device.DeviceKey;
+ }
+
+ return !!enum_result;
+}
+
+DesktopRect GetFullscreenRect() {
+ return DesktopRect::MakeXYWH(GetSystemMetrics(SM_XVIRTUALSCREEN),
+ GetSystemMetrics(SM_YVIRTUALSCREEN),
+ GetSystemMetrics(SM_CXVIRTUALSCREEN),
+ GetSystemMetrics(SM_CYVIRTUALSCREEN));
+}
+
+DesktopVector GetDpiForMonitor(HMONITOR monitor) {
+ UINT dpi_x, dpi_y;
+ // MDT_EFFECTIVE_DPI includes the scale factor as well as the system DPI.
+ HRESULT hr = ::GetDpiForMonitor(monitor, MDT_EFFECTIVE_DPI, &dpi_x, &dpi_y);
+ if (SUCCEEDED(hr)) {
+ return {static_cast<INT>(dpi_x), static_cast<INT>(dpi_y)};
+ }
+ RTC_LOG_GLE_EX(LS_WARNING, hr) << "GetDpiForMonitor() failed";
+
+ // If we can't get the per-monitor DPI, then return the system DPI.
+ HDC hdc = GetDC(nullptr);
+ if (hdc) {
+ DesktopVector dpi{GetDeviceCaps(hdc, LOGPIXELSX),
+ GetDeviceCaps(hdc, LOGPIXELSY)};
+ ReleaseDC(nullptr, hdc);
+ return dpi;
+ }
+
+ // If everything fails, then return the default DPI for Windows.
+ return {96, 96};
+}
+
+DesktopRect GetScreenRect(const DesktopCapturer::SourceId screen,
+ const std::wstring& device_key) {
+ if (screen == kFullDesktopScreenId) {
+ return GetFullscreenRect();
+ }
+
+ DISPLAY_DEVICEW device;
+ device.cb = sizeof(device);
+ BOOL result = EnumDisplayDevicesW(NULL, screen, &device, 0);
+ if (!result) {
+ return DesktopRect();
+ }
+
+ // Verifies the device index still maps to the same display device, to make
+ // sure we are capturing the same device when devices are added or removed.
+ // DeviceKey is documented as reserved, but it actually contains the registry
+ // key for the device and is unique for each monitor, while DeviceID is not.
+ if (device_key != device.DeviceKey) {
+ return DesktopRect();
+ }
+
+ DEVMODEW device_mode;
+ device_mode.dmSize = sizeof(device_mode);
+ device_mode.dmDriverExtra = 0;
+ result = EnumDisplaySettingsExW(device.DeviceName, ENUM_CURRENT_SETTINGS,
+ &device_mode, 0);
+ if (!result) {
+ return DesktopRect();
+ }
+
+ return DesktopRect::MakeXYWH(
+ device_mode.dmPosition.x, device_mode.dmPosition.y,
+ device_mode.dmPelsWidth, device_mode.dmPelsHeight);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.h b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.h
new file mode 100644
index 0000000000..71c79b9ab3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2014 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 MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_
+
+#if defined(WEBRTC_WIN)
+// Forward declare HMONITOR in a windows.h compatible way so that we can avoid
+// including windows.h.
+#define WEBRTC_DECLARE_HANDLE(name) \
+ struct name##__; \
+ typedef struct name##__* name
+WEBRTC_DECLARE_HANDLE(HMONITOR);
+#undef WEBRTC_DECLARE_HANDLE
+#endif
+
+#include <string>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// Returns true if the system has at least one active display.
+bool HasActiveDisplay();
+
+// Output the list of active screens into `screens`. Returns true if succeeded,
+// or false if it fails to enumerate the display devices. If the `device_names`
+// is provided, it will be filled with the DISPLAY_DEVICE.DeviceName in UTF-8
+// encoding. If this function returns true, consumers can always assume that
+// `screens`[i] and `device_names`[i] indicate the same monitor on the system.
+bool GetScreenList(DesktopCapturer::SourceList* screens,
+ std::vector<std::string>* device_names = nullptr);
+
+// Converts a device index (which are returned by `GetScreenList`) into an
+// HMONITOR.
+bool GetHmonitorFromDeviceIndex(DesktopCapturer::SourceId device_index,
+ HMONITOR* hmonitor);
+
+// Returns true if `monitor` represents a valid display
+// monitor. Consumers should recheck the validity of HMONITORs before use if a
+// WM_DISPLAYCHANGE message has been received.
+bool IsMonitorValid(HMONITOR monitor);
+
+// Returns the rect of the monitor identified by `monitor`, relative to the
+// primary display's top-left. On failure, returns an empty rect.
+DesktopRect GetMonitorRect(HMONITOR monitor);
+
+// Returns the DPI for the specified monitor. On failure, returns the system DPI
+// or the Windows default DPI (96x96) if the system DPI can't be retrieved.
+DesktopVector GetDpiForMonitor(HMONITOR monitor);
+
+// Returns true if `screen` is a valid screen. The screen device key is
+// returned through `device_key` if the screen is valid. The device key can be
+// used in GetScreenRect to verify the screen matches the previously obtained
+// id.
+bool IsScreenValid(DesktopCapturer::SourceId screen, std::wstring* device_key);
+
+// Get the rect of the entire system in system coordinate system. I.e. the
+// primary monitor always starts from (0, 0).
+DesktopRect GetFullscreenRect();
+
+// Get the rect of the screen identified by `screen`, relative to the primary
+// display's top-left. If the screen device key does not match `device_key`, or
+// the screen does not exist, or any error happens, an empty rect is returned.
+RTC_EXPORT DesktopRect GetScreenRect(DesktopCapturer::SourceId screen,
+ const std::wstring& device_key);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils_unittest.cc
new file mode 100644
index 0000000000..2e58c6b164
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils_unittest.cc
@@ -0,0 +1,81 @@
+/*
+ * 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 "modules/desktop_capture/win/screen_capture_utils.h"
+
+#include <string>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "rtc_base/logging.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(ScreenCaptureUtilsTest, GetScreenList) {
+ DesktopCapturer::SourceList screens;
+ std::vector<std::string> device_names;
+
+ ASSERT_TRUE(GetScreenList(&screens));
+ screens.clear();
+ ASSERT_TRUE(GetScreenList(&screens, &device_names));
+
+ ASSERT_EQ(screens.size(), device_names.size());
+}
+
+TEST(ScreenCaptureUtilsTest, DeviceIndexToHmonitor) {
+ DesktopCapturer::SourceList screens;
+ ASSERT_TRUE(GetScreenList(&screens));
+ if (screens.empty()) {
+ RTC_LOG(LS_INFO)
+ << "Skip ScreenCaptureUtilsTest on systems with no monitors.";
+ GTEST_SKIP();
+ }
+
+ HMONITOR hmonitor;
+ ASSERT_TRUE(GetHmonitorFromDeviceIndex(screens[0].id, &hmonitor));
+ ASSERT_TRUE(IsMonitorValid(hmonitor));
+}
+
+TEST(ScreenCaptureUtilsTest, FullScreenDeviceIndexToHmonitor) {
+ if (!HasActiveDisplay()) {
+ RTC_LOG(LS_INFO)
+ << "Skip ScreenCaptureUtilsTest on systems with no monitors.";
+ GTEST_SKIP();
+ }
+
+ HMONITOR hmonitor;
+ ASSERT_TRUE(GetHmonitorFromDeviceIndex(kFullDesktopScreenId, &hmonitor));
+ ASSERT_EQ(hmonitor, static_cast<HMONITOR>(0));
+ ASSERT_TRUE(IsMonitorValid(hmonitor));
+}
+
+TEST(ScreenCaptureUtilsTest, NoMonitors) {
+ if (HasActiveDisplay()) {
+ RTC_LOG(LS_INFO) << "Skip ScreenCaptureUtilsTest designed specifically for "
+ "systems with no monitors";
+ GTEST_SKIP();
+ }
+
+ HMONITOR hmonitor;
+ ASSERT_TRUE(GetHmonitorFromDeviceIndex(kFullDesktopScreenId, &hmonitor));
+ ASSERT_EQ(hmonitor, static_cast<HMONITOR>(0));
+
+ // The monitor should be invalid since the system has no attached displays.
+ ASSERT_FALSE(IsMonitorValid(hmonitor));
+}
+
+TEST(ScreenCaptureUtilsTest, InvalidDeviceIndexToHmonitor) {
+ HMONITOR hmonitor;
+ ASSERT_FALSE(GetHmonitorFromDeviceIndex(kInvalidScreenId, &hmonitor));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc
new file mode 100644
index 0000000000..0ed2e12423
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc
@@ -0,0 +1,246 @@
+/*
+ * 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 "modules/desktop_capture/win/screen_capturer_win_directx.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/trace_event.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+using Microsoft::WRL::ComPtr;
+
+// static
+bool ScreenCapturerWinDirectx::IsSupported() {
+ // Forwards IsSupported() function call to DxgiDuplicatorController.
+ return DxgiDuplicatorController::Instance()->IsSupported();
+}
+
+// static
+bool ScreenCapturerWinDirectx::RetrieveD3dInfo(D3dInfo* info) {
+ // Forwards SupportedFeatureLevels() function call to
+ // DxgiDuplicatorController.
+ return DxgiDuplicatorController::Instance()->RetrieveD3dInfo(info);
+}
+
+// static
+bool ScreenCapturerWinDirectx::IsCurrentSessionSupported() {
+ return DxgiDuplicatorController::IsCurrentSessionSupported();
+}
+
+// static
+bool ScreenCapturerWinDirectx::GetScreenListFromDeviceNames(
+ const std::vector<std::string>& device_names,
+ DesktopCapturer::SourceList* screens) {
+ RTC_DCHECK(screens->empty());
+
+ DesktopCapturer::SourceList gdi_screens;
+ std::vector<std::string> gdi_names;
+ if (!GetScreenList(&gdi_screens, &gdi_names)) {
+ return false;
+ }
+
+ RTC_DCHECK_EQ(gdi_screens.size(), gdi_names.size());
+
+ ScreenId max_screen_id = -1;
+ for (const DesktopCapturer::Source& screen : gdi_screens) {
+ max_screen_id = std::max(max_screen_id, screen.id);
+ }
+
+ for (const auto& device_name : device_names) {
+ const auto it = std::find(gdi_names.begin(), gdi_names.end(), device_name);
+ if (it == gdi_names.end()) {
+ // devices_names[i] has not been found in gdi_names, so use max_screen_id.
+ max_screen_id++;
+ screens->push_back({max_screen_id});
+ } else {
+ screens->push_back({gdi_screens[it - gdi_names.begin()]});
+ }
+ }
+
+ return true;
+}
+
+// static
+int ScreenCapturerWinDirectx::GetIndexFromScreenId(
+ ScreenId id,
+ const std::vector<std::string>& device_names) {
+ DesktopCapturer::SourceList screens;
+ if (!GetScreenListFromDeviceNames(device_names, &screens)) {
+ return -1;
+ }
+
+ RTC_DCHECK_EQ(device_names.size(), screens.size());
+
+ for (size_t i = 0; i < screens.size(); i++) {
+ if (screens[i].id == id) {
+ return static_cast<int>(i);
+ }
+ }
+
+ return -1;
+}
+
+ScreenCapturerWinDirectx::ScreenCapturerWinDirectx()
+ : controller_(DxgiDuplicatorController::Instance()) {}
+
+ScreenCapturerWinDirectx::ScreenCapturerWinDirectx(
+ const DesktopCaptureOptions& options)
+ : ScreenCapturerWinDirectx() {
+ options_ = options;
+}
+
+ScreenCapturerWinDirectx::~ScreenCapturerWinDirectx() = default;
+
+void ScreenCapturerWinDirectx::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinDirectx);
+
+ callback_ = callback;
+}
+
+void ScreenCapturerWinDirectx::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ shared_memory_factory_ = std::move(shared_memory_factory);
+}
+
+void ScreenCapturerWinDirectx::CaptureFrame() {
+ RTC_DCHECK(callback_);
+ TRACE_EVENT0("webrtc", "ScreenCapturerWinDirectx::CaptureFrame");
+
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ // Note that the [] operator will create the ScreenCaptureFrameQueue if it
+ // doesn't exist, so this is safe.
+ ScreenCaptureFrameQueue<DxgiFrame>& frames =
+ frame_queue_map_[current_screen_id_];
+
+ frames.MoveToNextFrame();
+
+ if (!frames.current_frame()) {
+ frames.ReplaceCurrentFrame(
+ std::make_unique<DxgiFrame>(shared_memory_factory_.get()));
+ }
+
+ DxgiDuplicatorController::Result result;
+ if (current_screen_id_ == kFullDesktopScreenId) {
+ result = controller_->Duplicate(frames.current_frame());
+ } else {
+ result = controller_->DuplicateMonitor(frames.current_frame(),
+ current_screen_id_);
+ }
+
+ using DuplicateResult = DxgiDuplicatorController::Result;
+ if (result != DuplicateResult::SUCCEEDED) {
+ RTC_LOG(LS_ERROR) << "DxgiDuplicatorController failed to capture desktop, "
+ "error code "
+ << DxgiDuplicatorController::ResultName(result);
+ }
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.DesktopCapture.Win.DirectXCapturerResult",
+ static_cast<int>(result),
+ static_cast<int>(DxgiDuplicatorController::Result::MAX_VALUE));
+ switch (result) {
+ case DuplicateResult::UNSUPPORTED_SESSION: {
+ RTC_LOG(LS_ERROR)
+ << "Current binary is running on a session not supported "
+ "by DirectX screen capturer.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ break;
+ }
+ case DuplicateResult::FRAME_PREPARE_FAILED: {
+ RTC_LOG(LS_ERROR) << "Failed to allocate a new DesktopFrame.";
+ // This usually means we do not have enough memory or SharedMemoryFactory
+ // cannot work correctly.
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ break;
+ }
+ case DuplicateResult::INVALID_MONITOR_ID: {
+ RTC_LOG(LS_ERROR) << "Invalid monitor id " << current_screen_id_;
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ break;
+ }
+ case DuplicateResult::INITIALIZATION_FAILED:
+ case DuplicateResult::DUPLICATION_FAILED: {
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ break;
+ }
+ case DuplicateResult::SUCCEEDED: {
+ std::unique_ptr<DesktopFrame> frame =
+ frames.current_frame()->frame()->Share();
+
+ int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec;
+ RTC_HISTOGRAM_COUNTS_1000(
+ "WebRTC.DesktopCapture.Win.DirectXCapturerFrameTime",
+ capture_time_ms);
+ frame->set_capture_time_ms(capture_time_ms);
+ frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinDirectx);
+ // The DXGI Output Duplicator supports embedding the cursor but it is
+ // only supported on very few display adapters. This switch allows us
+ // to exclude an integrated cursor for all captured frames.
+ if (!options_.prefer_cursor_embedded()) {
+ frame->set_may_contain_cursor(false);
+ }
+
+ // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on
+ // the frame, see WindowCapturerMac::CaptureFrame.
+
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+ break;
+ }
+ }
+}
+
+bool ScreenCapturerWinDirectx::GetSourceList(SourceList* sources) {
+ std::vector<std::string> device_names;
+ if (!controller_->GetDeviceNames(&device_names)) {
+ return false;
+ }
+
+ return GetScreenListFromDeviceNames(device_names, sources);
+}
+
+bool ScreenCapturerWinDirectx::SelectSource(SourceId id) {
+ if (id == kFullDesktopScreenId) {
+ current_screen_id_ = id;
+ return true;
+ }
+
+ std::vector<std::string> device_names;
+ if (!controller_->GetDeviceNames(&device_names)) {
+ return false;
+ }
+
+ int index;
+ index = GetIndexFromScreenId(id, device_names);
+ if (index == -1) {
+ return false;
+ }
+
+ current_screen_id_ = index;
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.h b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.h
new file mode 100644
index 0000000000..a231643c33
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.h
@@ -0,0 +1,108 @@
+/*
+ * 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 MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_DIRECTX_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_DIRECTX_H_
+
+#include <d3dcommon.h>
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/win/dxgi_duplicator_controller.h"
+#include "modules/desktop_capture/win/dxgi_frame.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// ScreenCapturerWinDirectx captures 32bit RGBA using DirectX.
+class RTC_EXPORT ScreenCapturerWinDirectx : public DesktopCapturer {
+ public:
+ using D3dInfo = DxgiDuplicatorController::D3dInfo;
+
+ // Whether the system supports DirectX based capturing.
+ static bool IsSupported();
+
+ // Returns a most recent D3dInfo composed by
+ // DxgiDuplicatorController::Initialize() function. This function implicitly
+ // calls DxgiDuplicatorController::Initialize() if it has not been
+ // initialized. This function returns false and output parameter is kept
+ // unchanged if DxgiDuplicatorController::Initialize() failed.
+ // The D3dInfo may change based on hardware configuration even without
+ // restarting the hardware and software. Refer to https://goo.gl/OOCppq. So
+ // consumers should not cache the result returned by this function.
+ static bool RetrieveD3dInfo(D3dInfo* info);
+
+ // Whether current process is running in a Windows session which is supported
+ // by ScreenCapturerWinDirectx.
+ // Usually using ScreenCapturerWinDirectx in unsupported sessions will fail.
+ // But this behavior may vary on different Windows version. So consumers can
+ // always try IsSupported() function.
+ static bool IsCurrentSessionSupported();
+
+ // Maps `device_names` with the result from GetScreenList() and creates a new
+ // SourceList to include only the ones in `device_names`. If this function
+ // returns true, consumers can always assume `device_names`.size() equals to
+ // `screens`->size(), meanwhile `device_names`[i] and `screens`[i] indicate
+ // the same monitor on the system.
+ // Public for test only.
+ static bool GetScreenListFromDeviceNames(
+ const std::vector<std::string>& device_names,
+ DesktopCapturer::SourceList* screens);
+
+ // Maps `id` with the result from GetScreenListFromDeviceNames() and returns
+ // the index of the entity in `device_names`. This function returns -1 if `id`
+ // cannot be found.
+ // Public for test only.
+ static int GetIndexFromScreenId(ScreenId id,
+ const std::vector<std::string>& device_names);
+
+ // This constructor is deprecated. Please don't use it in new implementations.
+ ScreenCapturerWinDirectx();
+ explicit ScreenCapturerWinDirectx(const DesktopCaptureOptions& options);
+
+ ~ScreenCapturerWinDirectx() override;
+
+ ScreenCapturerWinDirectx(const ScreenCapturerWinDirectx&) = delete;
+ ScreenCapturerWinDirectx& operator=(const ScreenCapturerWinDirectx&) = delete;
+
+ // DesktopCapturer implementation.
+ void Start(Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ const rtc::scoped_refptr<DxgiDuplicatorController> controller_;
+ DesktopCaptureOptions options_;
+
+ // The underlying DxgiDuplicators may retain a reference to the frames that
+ // we ask them to duplicate so that they can continue returning valid frames
+ // in the event that the target has not been updated. Thus, we need to ensure
+ // that we have a separate frame queue for each source id, so that these held
+ // frames don't get overwritten with the data from another Duplicator/monitor.
+ std::unordered_map<SourceId, ScreenCaptureFrameQueue<DxgiFrame>>
+ frame_queue_map_;
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
+ Callback* callback_ = nullptr;
+ SourceId current_screen_id_ = kFullDesktopScreenId;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_DIRECTX_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx_unittest.cc
new file mode 100644
index 0000000000..c9f46f782c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx_unittest.cc
@@ -0,0 +1,41 @@
+/*
+ * 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 "modules/desktop_capture/win/screen_capturer_win_directx.h"
+
+#include <string>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+// This test cannot ensure GetScreenListFromDeviceNames() won't reorder the
+// devices in its output, since the device name is missing.
+TEST(ScreenCaptureUtilsTest, GetScreenListFromDeviceNamesAndGetIndex) {
+ const std::vector<std::string> device_names = {
+ "\\\\.\\DISPLAY0",
+ "\\\\.\\DISPLAY1",
+ "\\\\.\\DISPLAY2",
+ };
+ DesktopCapturer::SourceList screens;
+ ASSERT_TRUE(ScreenCapturerWinDirectx::GetScreenListFromDeviceNames(
+ device_names, &screens));
+ ASSERT_EQ(device_names.size(), screens.size());
+
+ for (size_t i = 0; i < screens.size(); i++) {
+ ASSERT_EQ(ScreenCapturerWinDirectx::GetIndexFromScreenId(screens[i].id,
+ device_names),
+ static_cast<int>(i));
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc
new file mode 100644
index 0000000000..4d07b6b92f
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2014 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 "modules/desktop_capture/win/screen_capturer_win_gdi.h"
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_frame_win.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/win/cursor.h"
+#include "modules/desktop_capture/win/desktop.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/trace_event.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+namespace {
+
+// Constants from dwmapi.h.
+const UINT DWM_EC_DISABLECOMPOSITION = 0;
+const UINT DWM_EC_ENABLECOMPOSITION = 1;
+
+const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll";
+
+} // namespace
+
+ScreenCapturerWinGdi::ScreenCapturerWinGdi(
+ const DesktopCaptureOptions& options) {
+ if (options.disable_effects()) {
+ // Load dwmapi.dll dynamically since it is not available on XP.
+ if (!dwmapi_library_)
+ dwmapi_library_ = LoadLibraryW(kDwmapiLibraryName);
+
+ if (dwmapi_library_) {
+ composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
+ GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
+ }
+ }
+}
+
+ScreenCapturerWinGdi::~ScreenCapturerWinGdi() {
+ if (desktop_dc_)
+ ReleaseDC(NULL, desktop_dc_);
+ if (memory_dc_)
+ DeleteDC(memory_dc_);
+
+ // Restore Aero.
+ if (composition_func_)
+ (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
+
+ if (dwmapi_library_)
+ FreeLibrary(dwmapi_library_);
+}
+
+void ScreenCapturerWinGdi::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ shared_memory_factory_ = std::move(shared_memory_factory);
+}
+
+void ScreenCapturerWinGdi::CaptureFrame() {
+ TRACE_EVENT0("webrtc", "ScreenCapturerWinGdi::CaptureFrame");
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ queue_.MoveToNextFrame();
+ if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
+ RTC_DLOG(LS_WARNING) << "Overwriting frame that is still shared.";
+ }
+
+ // Make sure the GDI capture resources are up-to-date.
+ PrepareCaptureResources();
+
+ if (!CaptureImage()) {
+ RTC_LOG(LS_WARNING) << "Failed to capture screen by GDI.";
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ // Emit the current frame.
+ std::unique_ptr<DesktopFrame> frame = queue_.current_frame()->Share();
+ frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
+ GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
+ frame->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(frame->size()));
+
+ int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec;
+ RTC_HISTOGRAM_COUNTS_1000(
+ "WebRTC.DesktopCapture.Win.ScreenGdiCapturerFrameTime", capture_time_ms);
+ frame->set_capture_time_ms(capture_time_ms);
+ frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinGdi);
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+}
+
+bool ScreenCapturerWinGdi::GetSourceList(SourceList* sources) {
+ return webrtc::GetScreenList(sources);
+}
+
+bool ScreenCapturerWinGdi::SelectSource(SourceId id) {
+ bool valid = IsScreenValid(id, &current_device_key_);
+ if (valid)
+ current_screen_id_ = id;
+ return valid;
+}
+
+void ScreenCapturerWinGdi::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinGdi);
+
+ callback_ = callback;
+
+ // Vote to disable Aero composited desktop effects while capturing. Windows
+ // will restore Aero automatically if the process exits. This has no effect
+ // under Windows 8 or higher. See crbug.com/124018.
+ if (composition_func_)
+ (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
+}
+
+void ScreenCapturerWinGdi::PrepareCaptureResources() {
+ // Switch to the desktop receiving user input if different from the current
+ // one.
+ std::unique_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
+ if (input_desktop && !desktop_.IsSame(*input_desktop)) {
+ // Release GDI resources otherwise SetThreadDesktop will fail.
+ if (desktop_dc_) {
+ ReleaseDC(NULL, desktop_dc_);
+ desktop_dc_ = nullptr;
+ }
+
+ if (memory_dc_) {
+ DeleteDC(memory_dc_);
+ memory_dc_ = nullptr;
+ }
+
+ // If SetThreadDesktop() fails, the thread is still assigned a desktop.
+ // So we can continue capture screen bits, just from the wrong desktop.
+ desktop_.SetThreadDesktop(input_desktop.release());
+
+ // Re-assert our vote to disable Aero.
+ // See crbug.com/124018 and crbug.com/129906.
+ if (composition_func_) {
+ (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
+ }
+ }
+
+ // If the display configurations have changed then recreate GDI resources.
+ if (display_configuration_monitor_.IsChanged(kFullDesktopScreenId)) {
+ if (desktop_dc_) {
+ ReleaseDC(NULL, desktop_dc_);
+ desktop_dc_ = nullptr;
+ }
+ if (memory_dc_) {
+ DeleteDC(memory_dc_);
+ memory_dc_ = nullptr;
+ }
+ }
+
+ if (!desktop_dc_) {
+ RTC_DCHECK(!memory_dc_);
+
+ // Create GDI device contexts to capture from the desktop into memory.
+ desktop_dc_ = GetDC(nullptr);
+ RTC_CHECK(desktop_dc_);
+ memory_dc_ = CreateCompatibleDC(desktop_dc_);
+ RTC_CHECK(memory_dc_);
+
+ // Make sure the frame buffers will be reallocated.
+ queue_.Reset();
+ }
+}
+
+bool ScreenCapturerWinGdi::CaptureImage() {
+ DesktopRect screen_rect =
+ GetScreenRect(current_screen_id_, current_device_key_);
+ if (screen_rect.is_empty()) {
+ RTC_LOG(LS_WARNING) << "Failed to get screen rect.";
+ return false;
+ }
+
+ DesktopSize size = screen_rect.size();
+ // If the current buffer is from an older generation then allocate a new one.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (!queue_.current_frame() ||
+ !queue_.current_frame()->size().equals(screen_rect.size())) {
+ RTC_DCHECK(desktop_dc_);
+ RTC_DCHECK(memory_dc_);
+
+ std::unique_ptr<DesktopFrame> buffer = DesktopFrameWin::Create(
+ size, shared_memory_factory_.get(), desktop_dc_);
+ if (!buffer) {
+ RTC_LOG(LS_WARNING) << "Failed to create frame buffer.";
+ return false;
+ }
+ queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(buffer)));
+ }
+ queue_.current_frame()->set_top_left(
+ screen_rect.top_left().subtract(GetFullscreenRect().top_left()));
+
+ // Select the target bitmap into the memory dc and copy the rect from desktop
+ // to memory.
+ DesktopFrameWin* current = static_cast<DesktopFrameWin*>(
+ queue_.current_frame()->GetUnderlyingFrame());
+ HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap());
+ if (!previous_object || previous_object == HGDI_ERROR) {
+ RTC_LOG(LS_WARNING) << "Failed to select current bitmap into memery dc.";
+ return false;
+ }
+
+ bool result = (BitBlt(memory_dc_, 0, 0, screen_rect.width(),
+ screen_rect.height(), desktop_dc_, screen_rect.left(),
+ screen_rect.top(), SRCCOPY | CAPTUREBLT) != FALSE);
+ if (!result) {
+ RTC_LOG_GLE(LS_WARNING) << "BitBlt failed";
+ }
+
+ // Select back the previously selected object to that the device contect
+ // could be destroyed independently of the bitmap if needed.
+ SelectObject(memory_dc_, previous_object);
+
+ return result;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h
new file mode 100644
index 0000000000..7c3977ed42
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014 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 MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_
+
+#include <windows.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/win/display_configuration_monitor.h"
+#include "modules/desktop_capture/win/scoped_thread_desktop.h"
+
+namespace webrtc {
+
+// ScreenCapturerWinGdi captures 32bit RGB using GDI.
+//
+// ScreenCapturerWinGdi is double-buffered as required by ScreenCapturer.
+// This class does not detect DesktopFrame::updated_region(), the field is
+// always set to the entire frame rectangle. ScreenCapturerDifferWrapper should
+// be used if that functionality is necessary.
+class ScreenCapturerWinGdi : public DesktopCapturer {
+ public:
+ explicit ScreenCapturerWinGdi(const DesktopCaptureOptions& options);
+ ~ScreenCapturerWinGdi() override;
+
+ ScreenCapturerWinGdi(const ScreenCapturerWinGdi&) = delete;
+ ScreenCapturerWinGdi& operator=(const ScreenCapturerWinGdi&) = delete;
+
+ // Overridden from ScreenCapturer:
+ void Start(Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ typedef HRESULT(WINAPI* DwmEnableCompositionFunc)(UINT);
+
+ // Make sure that the device contexts match the screen configuration.
+ void PrepareCaptureResources();
+
+ // Captures the current screen contents into the current buffer. Returns true
+ // if succeeded.
+ bool CaptureImage();
+
+ // Capture the current cursor shape.
+ void CaptureCursor();
+
+ Callback* callback_ = nullptr;
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
+ SourceId current_screen_id_ = kFullDesktopScreenId;
+ std::wstring current_device_key_;
+
+ ScopedThreadDesktop desktop_;
+
+ // GDI resources used for screen capture.
+ HDC desktop_dc_ = NULL;
+ HDC memory_dc_ = NULL;
+
+ // Queue of the frames buffers.
+ ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
+
+ DisplayConfigurationMonitor display_configuration_monitor_;
+
+ HMODULE dwmapi_library_ = NULL;
+ DwmEnableCompositionFunc composition_func_ = nullptr;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
new file mode 100644
index 0000000000..214eb0e463
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2014 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 "modules/desktop_capture/win/screen_capturer_win_magnifier.h"
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_frame_win.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/win/cursor.h"
+#include "modules/desktop_capture/win/desktop.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+namespace {
+DWORD GetTlsIndex() {
+ static const DWORD tls_index = TlsAlloc();
+ RTC_DCHECK(tls_index != TLS_OUT_OF_INDEXES);
+ return tls_index;
+}
+
+} // namespace
+
+// kMagnifierWindowClass has to be "Magnifier" according to the Magnification
+// API. The other strings can be anything.
+static wchar_t kMagnifierHostClass[] = L"ScreenCapturerWinMagnifierHost";
+static wchar_t kHostWindowName[] = L"MagnifierHost";
+static wchar_t kMagnifierWindowClass[] = L"Magnifier";
+static wchar_t kMagnifierWindowName[] = L"MagnifierWindow";
+
+ScreenCapturerWinMagnifier::ScreenCapturerWinMagnifier() = default;
+ScreenCapturerWinMagnifier::~ScreenCapturerWinMagnifier() {
+ // DestroyWindow must be called before MagUninitialize. magnifier_window_ is
+ // destroyed automatically when host_window_ is destroyed.
+ if (host_window_)
+ DestroyWindow(host_window_);
+
+ if (magnifier_initialized_)
+ mag_uninitialize_func_();
+
+ if (mag_lib_handle_)
+ FreeLibrary(mag_lib_handle_);
+
+ if (desktop_dc_)
+ ReleaseDC(NULL, desktop_dc_);
+}
+
+void ScreenCapturerWinMagnifier::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinMagnifier);
+
+ callback_ = callback;
+
+ if (!InitializeMagnifier()) {
+ RTC_LOG_F(LS_WARNING) << "Magnifier initialization failed.";
+ }
+}
+
+void ScreenCapturerWinMagnifier::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ shared_memory_factory_ = std::move(shared_memory_factory);
+}
+
+void ScreenCapturerWinMagnifier::CaptureFrame() {
+ RTC_DCHECK(callback_);
+ if (!magnifier_initialized_) {
+ RTC_LOG_F(LS_WARNING) << "Magnifier initialization failed.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ // Switch to the desktop receiving user input if different from the current
+ // one.
+ std::unique_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
+ if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
+ // Release GDI resources otherwise SetThreadDesktop will fail.
+ if (desktop_dc_) {
+ ReleaseDC(NULL, desktop_dc_);
+ desktop_dc_ = NULL;
+ }
+ // If SetThreadDesktop() fails, the thread is still assigned a desktop.
+ // So we can continue capture screen bits, just from the wrong desktop.
+ desktop_.SetThreadDesktop(input_desktop.release());
+ }
+
+ DesktopRect rect = GetScreenRect(current_screen_id_, current_device_key_);
+ queue_.MoveToNextFrame();
+ CreateCurrentFrameIfNecessary(rect.size());
+ // CaptureImage may fail in some situations, e.g. windows8 metro mode. So
+ // defer to the fallback capturer if magnifier capturer did not work.
+ if (!CaptureImage(rect)) {
+ RTC_LOG_F(LS_WARNING) << "Magnifier capturer failed to capture a frame.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ // Emit the current frame.
+ std::unique_ptr<DesktopFrame> frame = queue_.current_frame()->Share();
+ frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
+ GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
+ frame->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(frame->size()));
+
+ int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec;
+ RTC_HISTOGRAM_COUNTS_1000(
+ "WebRTC.DesktopCapture.Win.MagnifierCapturerFrameTime", capture_time_ms);
+ frame->set_capture_time_ms(capture_time_ms);
+ frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinMagnifier);
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+}
+
+bool ScreenCapturerWinMagnifier::GetSourceList(SourceList* sources) {
+ return webrtc::GetScreenList(sources);
+}
+
+bool ScreenCapturerWinMagnifier::SelectSource(SourceId id) {
+ if (IsScreenValid(id, &current_device_key_)) {
+ current_screen_id_ = id;
+ return true;
+ }
+
+ return false;
+}
+
+void ScreenCapturerWinMagnifier::SetExcludedWindow(WindowId excluded_window) {
+ excluded_window_ = (HWND)excluded_window;
+ if (excluded_window_ && magnifier_initialized_) {
+ set_window_filter_list_func_(magnifier_window_, MW_FILTERMODE_EXCLUDE, 1,
+ &excluded_window_);
+ }
+}
+
+bool ScreenCapturerWinMagnifier::CaptureImage(const DesktopRect& rect) {
+ RTC_DCHECK(magnifier_initialized_);
+
+ // Set the magnifier control to cover the captured rect. The content of the
+ // magnifier control will be the captured image.
+ BOOL result = SetWindowPos(magnifier_window_, NULL, rect.left(), rect.top(),
+ rect.width(), rect.height(), 0);
+ if (!result) {
+ RTC_LOG_F(LS_WARNING) << "Failed to call SetWindowPos: " << GetLastError()
+ << ". Rect = {" << rect.left() << ", " << rect.top()
+ << ", " << rect.right() << ", " << rect.bottom()
+ << "}";
+ return false;
+ }
+
+ magnifier_capture_succeeded_ = false;
+
+ RECT native_rect = {rect.left(), rect.top(), rect.right(), rect.bottom()};
+
+ TlsSetValue(GetTlsIndex(), this);
+ // OnCaptured will be called via OnMagImageScalingCallback and fill in the
+ // frame before set_window_source_func_ returns.
+ result = set_window_source_func_(magnifier_window_, native_rect);
+
+ if (!result) {
+ RTC_LOG_F(LS_WARNING) << "Failed to call MagSetWindowSource: "
+ << GetLastError() << ". Rect = {" << rect.left()
+ << ", " << rect.top() << ", " << rect.right() << ", "
+ << rect.bottom() << "}";
+ return false;
+ }
+
+ return magnifier_capture_succeeded_;
+}
+
+BOOL ScreenCapturerWinMagnifier::OnMagImageScalingCallback(
+ HWND hwnd,
+ void* srcdata,
+ MAGIMAGEHEADER srcheader,
+ void* destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty) {
+ ScreenCapturerWinMagnifier* owner =
+ reinterpret_cast<ScreenCapturerWinMagnifier*>(TlsGetValue(GetTlsIndex()));
+ TlsSetValue(GetTlsIndex(), nullptr);
+ owner->OnCaptured(srcdata, srcheader);
+
+ return TRUE;
+}
+
+// TODO(zijiehe): These functions are available on Windows Vista or upper, so we
+// do not need to use LoadLibrary and GetProcAddress anymore. Use regular
+// include and function calls instead of a dynamical loaded library.
+bool ScreenCapturerWinMagnifier::InitializeMagnifier() {
+ RTC_DCHECK(!magnifier_initialized_);
+
+ if (GetSystemMetrics(SM_CMONITORS) != 1) {
+ // Do not try to use the magnifier in multi-screen setup (where the API
+ // crashes sometimes).
+ RTC_LOG_F(LS_WARNING) << "Magnifier capturer cannot work on multi-screen "
+ "system.";
+ return false;
+ }
+
+ desktop_dc_ = GetDC(nullptr);
+
+ mag_lib_handle_ = LoadLibraryW(L"Magnification.dll");
+ if (!mag_lib_handle_)
+ return false;
+
+ // Initialize Magnification API function pointers.
+ mag_initialize_func_ = reinterpret_cast<MagInitializeFunc>(
+ GetProcAddress(mag_lib_handle_, "MagInitialize"));
+ mag_uninitialize_func_ = reinterpret_cast<MagUninitializeFunc>(
+ GetProcAddress(mag_lib_handle_, "MagUninitialize"));
+ set_window_source_func_ = reinterpret_cast<MagSetWindowSourceFunc>(
+ GetProcAddress(mag_lib_handle_, "MagSetWindowSource"));
+ set_window_filter_list_func_ = reinterpret_cast<MagSetWindowFilterListFunc>(
+ GetProcAddress(mag_lib_handle_, "MagSetWindowFilterList"));
+ set_image_scaling_callback_func_ =
+ reinterpret_cast<MagSetImageScalingCallbackFunc>(
+ GetProcAddress(mag_lib_handle_, "MagSetImageScalingCallback"));
+
+ if (!mag_initialize_func_ || !mag_uninitialize_func_ ||
+ !set_window_source_func_ || !set_window_filter_list_func_ ||
+ !set_image_scaling_callback_func_) {
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "library functions missing.";
+ return false;
+ }
+
+ BOOL result = mag_initialize_func_();
+ if (!result) {
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from MagInitialize "
+ << GetLastError();
+ return false;
+ }
+
+ HMODULE hInstance = nullptr;
+ result =
+ GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<char*>(&DefWindowProc), &hInstance);
+ if (!result) {
+ mag_uninitialize_func_();
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from GetModulehandleExA "
+ << GetLastError();
+ return false;
+ }
+
+ // Register the host window class. See the MSDN documentation of the
+ // Magnification API for more infomation.
+ WNDCLASSEXW wcex = {};
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.lpfnWndProc = &DefWindowProc;
+ wcex.hInstance = hInstance;
+ wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wcex.lpszClassName = kMagnifierHostClass;
+
+ // Ignore the error which may happen when the class is already registered.
+ RegisterClassExW(&wcex);
+
+ // Create the host window.
+ host_window_ =
+ CreateWindowExW(WS_EX_LAYERED, kMagnifierHostClass, kHostWindowName, 0, 0,
+ 0, 0, 0, nullptr, nullptr, hInstance, nullptr);
+ if (!host_window_) {
+ mag_uninitialize_func_();
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from creating host window "
+ << GetLastError();
+ return false;
+ }
+
+ // Create the magnifier control.
+ magnifier_window_ = CreateWindowW(kMagnifierWindowClass, kMagnifierWindowName,
+ WS_CHILD | WS_VISIBLE, 0, 0, 0, 0,
+ host_window_, nullptr, hInstance, nullptr);
+ if (!magnifier_window_) {
+ mag_uninitialize_func_();
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from creating magnifier window "
+ << GetLastError();
+ return false;
+ }
+
+ // Hide the host window.
+ ShowWindow(host_window_, SW_HIDE);
+
+ // Set the scaling callback to receive captured image.
+ result = set_image_scaling_callback_func_(
+ magnifier_window_,
+ &ScreenCapturerWinMagnifier::OnMagImageScalingCallback);
+ if (!result) {
+ mag_uninitialize_func_();
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from MagSetImageScalingCallback "
+ << GetLastError();
+ return false;
+ }
+
+ if (excluded_window_) {
+ result = set_window_filter_list_func_(
+ magnifier_window_, MW_FILTERMODE_EXCLUDE, 1, &excluded_window_);
+ if (!result) {
+ mag_uninitialize_func_();
+ RTC_LOG_F(LS_WARNING)
+ << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from MagSetWindowFilterList "
+ << GetLastError();
+ return false;
+ }
+ }
+
+ magnifier_initialized_ = true;
+ return true;
+}
+
+void ScreenCapturerWinMagnifier::OnCaptured(void* data,
+ const MAGIMAGEHEADER& header) {
+ DesktopFrame* current_frame = queue_.current_frame();
+
+ // Verify the format.
+ // TODO(jiayl): support capturing sources with pixel formats other than RGBA.
+ int captured_bytes_per_pixel = header.cbSize / header.width / header.height;
+ if (header.format != GUID_WICPixelFormat32bppRGBA ||
+ header.width != static_cast<UINT>(current_frame->size().width()) ||
+ header.height != static_cast<UINT>(current_frame->size().height()) ||
+ header.stride != static_cast<UINT>(current_frame->stride()) ||
+ captured_bytes_per_pixel != DesktopFrame::kBytesPerPixel) {
+ RTC_LOG_F(LS_WARNING)
+ << "Output format does not match the captured format: "
+ "width = "
+ << header.width
+ << ", "
+ "height = "
+ << header.height
+ << ", "
+ "stride = "
+ << header.stride
+ << ", "
+ "bpp = "
+ << captured_bytes_per_pixel
+ << ", "
+ "pixel format RGBA ? "
+ << (header.format == GUID_WICPixelFormat32bppRGBA) << ".";
+ return;
+ }
+
+ // Copy the data into the frame.
+ current_frame->CopyPixelsFrom(
+ reinterpret_cast<uint8_t*>(data), header.stride,
+ DesktopRect::MakeXYWH(0, 0, header.width, header.height));
+
+ magnifier_capture_succeeded_ = true;
+}
+
+void ScreenCapturerWinMagnifier::CreateCurrentFrameIfNecessary(
+ const DesktopSize& size) {
+ // If the current buffer is from an older generation then allocate a new one.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (!queue_.current_frame() || !queue_.current_frame()->size().equals(size)) {
+ std::unique_ptr<DesktopFrame> frame =
+ shared_memory_factory_
+ ? SharedMemoryDesktopFrame::Create(size,
+ shared_memory_factory_.get())
+ : std::unique_ptr<DesktopFrame>(new BasicDesktopFrame(size));
+ queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(frame)));
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h
new file mode 100644
index 0000000000..07c5b1e9e6
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2014 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 MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_
+
+#include <magnification.h>
+#include <wincodec.h>
+#include <windows.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/screen_capturer_helper.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/win/scoped_thread_desktop.h"
+
+namespace webrtc {
+
+class DesktopFrame;
+class DesktopRect;
+
+// Captures the screen using the Magnification API to support window exclusion.
+// Each capturer must run on a dedicated thread because it uses thread local
+// storage for redirecting the library callback. Also the thread must have a UI
+// message loop to handle the window messages for the magnifier window.
+//
+// This class does not detect DesktopFrame::updated_region(), the field is
+// always set to the entire frame rectangle. ScreenCapturerDifferWrapper should
+// be used if that functionality is necessary.
+class ScreenCapturerWinMagnifier : public DesktopCapturer {
+ public:
+ ScreenCapturerWinMagnifier();
+ ~ScreenCapturerWinMagnifier() override;
+
+ ScreenCapturerWinMagnifier(const ScreenCapturerWinMagnifier&) = delete;
+ ScreenCapturerWinMagnifier& operator=(const ScreenCapturerWinMagnifier&) =
+ delete;
+
+ // Overridden from ScreenCapturer:
+ void Start(Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* screens) override;
+ bool SelectSource(SourceId id) override;
+ void SetExcludedWindow(WindowId window) override;
+
+ private:
+ typedef BOOL(WINAPI* MagImageScalingCallback)(HWND hwnd,
+ void* srcdata,
+ MAGIMAGEHEADER srcheader,
+ void* destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty);
+ typedef BOOL(WINAPI* MagInitializeFunc)(void);
+ typedef BOOL(WINAPI* MagUninitializeFunc)(void);
+ typedef BOOL(WINAPI* MagSetWindowSourceFunc)(HWND hwnd, RECT rect);
+ typedef BOOL(WINAPI* MagSetWindowFilterListFunc)(HWND hwnd,
+ DWORD dwFilterMode,
+ int count,
+ HWND* pHWND);
+ typedef BOOL(WINAPI* MagSetImageScalingCallbackFunc)(
+ HWND hwnd,
+ MagImageScalingCallback callback);
+
+ static BOOL WINAPI OnMagImageScalingCallback(HWND hwnd,
+ void* srcdata,
+ MAGIMAGEHEADER srcheader,
+ void* destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty);
+
+ // Captures the screen within `rect` in the desktop coordinates. Returns true
+ // if succeeded.
+ // It can only capture the primary screen for now. The magnification library
+ // crashes under some screen configurations (e.g. secondary screen on top of
+ // primary screen) if it tries to capture a non-primary screen. The caller
+ // must make sure not calling it on non-primary screens.
+ bool CaptureImage(const DesktopRect& rect);
+
+ // Helper method for setting up the magnifier control. Returns true if
+ // succeeded.
+ bool InitializeMagnifier();
+
+ // Called by OnMagImageScalingCallback to output captured data.
+ void OnCaptured(void* data, const MAGIMAGEHEADER& header);
+
+ // Makes sure the current frame exists and matches `size`.
+ void CreateCurrentFrameIfNecessary(const DesktopSize& size);
+
+ Callback* callback_ = nullptr;
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
+ ScreenId current_screen_id_ = kFullDesktopScreenId;
+ std::wstring current_device_key_;
+ HWND excluded_window_ = NULL;
+
+ // Queue of the frames buffers.
+ ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
+
+ ScopedThreadDesktop desktop_;
+
+ // Used for getting the screen dpi.
+ HDC desktop_dc_ = NULL;
+
+ HMODULE mag_lib_handle_ = NULL;
+ MagInitializeFunc mag_initialize_func_ = nullptr;
+ MagUninitializeFunc mag_uninitialize_func_ = nullptr;
+ MagSetWindowSourceFunc set_window_source_func_ = nullptr;
+ MagSetWindowFilterListFunc set_window_filter_list_func_ = nullptr;
+ MagSetImageScalingCallbackFunc set_image_scaling_callback_func_ = nullptr;
+
+ // The hidden window hosting the magnifier control.
+ HWND host_window_ = NULL;
+ // The magnifier control that captures the screen.
+ HWND magnifier_window_ = NULL;
+
+ // True if the magnifier control has been successfully initialized.
+ bool magnifier_initialized_ = false;
+
+ // True if the last OnMagImageScalingCallback was called and handled
+ // successfully. Reset at the beginning of each CaptureImage call.
+ bool magnifier_capture_succeeded_ = true;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.cc b/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.cc
new file mode 100644
index 0000000000..398ea1e53a
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 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 "modules/desktop_capture/win/selected_window_context.h"
+
+namespace webrtc {
+
+SelectedWindowContext::SelectedWindowContext(
+ HWND selected_window,
+ DesktopRect selected_window_rect,
+ WindowCaptureHelperWin* window_capture_helper)
+ : selected_window_(selected_window),
+ selected_window_rect_(selected_window_rect),
+ window_capture_helper_(window_capture_helper) {
+ selected_window_thread_id_ =
+ GetWindowThreadProcessId(selected_window, &selected_window_process_id_);
+}
+
+bool SelectedWindowContext::IsSelectedWindowValid() const {
+ return selected_window_thread_id_ != 0;
+}
+
+bool SelectedWindowContext::IsWindowOwnedBySelectedWindow(HWND hwnd) const {
+ // This check works for drop-down menus & dialog pop-up windows.
+ if (GetAncestor(hwnd, GA_ROOTOWNER) == selected_window_) {
+ return true;
+ }
+
+ // Assume that all other windows are unrelated to the selected window.
+ // This will cause some windows that are actually related to be missed,
+ // e.g. context menus and tool-tips, but avoids the risk of capturing
+ // unrelated windows. Using heuristics such as matching the thread and
+ // process Ids suffers from false-positives, e.g. in multi-document
+ // applications.
+
+ return false;
+}
+
+bool SelectedWindowContext::IsWindowOverlappingSelectedWindow(HWND hwnd) const {
+ return window_capture_helper_->AreWindowsOverlapping(hwnd, selected_window_,
+ selected_window_rect_);
+}
+
+HWND SelectedWindowContext::selected_window() const {
+ return selected_window_;
+}
+
+WindowCaptureHelperWin* SelectedWindowContext::window_capture_helper() const {
+ return window_capture_helper_;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.h b/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.h
new file mode 100644
index 0000000000..99e38e3fa2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 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 MODULES_DESKTOP_CAPTURE_WIN_SELECTED_WINDOW_CONTEXT_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SELECTED_WINDOW_CONTEXT_H_
+
+#include <windows.h>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+
+namespace webrtc {
+
+class SelectedWindowContext {
+ public:
+ SelectedWindowContext(HWND selected_window,
+ DesktopRect selected_window_rect,
+ WindowCaptureHelperWin* window_capture_helper);
+
+ bool IsSelectedWindowValid() const;
+
+ bool IsWindowOwnedBySelectedWindow(HWND hwnd) const;
+ bool IsWindowOverlappingSelectedWindow(HWND hwnd) const;
+
+ HWND selected_window() const;
+ WindowCaptureHelperWin* window_capture_helper() const;
+
+ private:
+ const HWND selected_window_;
+ const DesktopRect selected_window_rect_;
+ WindowCaptureHelperWin* const window_capture_helper_;
+ DWORD selected_window_thread_id_;
+ DWORD selected_window_process_id_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SELECTED_WINDOW_CONTEXT_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.cc b/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.cc
new file mode 100644
index 0000000000..c07ff74aa5
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2020 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 "modules/desktop_capture/win/test_support/test_window.h"
+
+namespace webrtc {
+namespace {
+
+const WCHAR kWindowClass[] = L"DesktopCaptureTestWindowClass";
+const int kWindowHeight = 200;
+const int kWindowWidth = 300;
+
+LRESULT CALLBACK WindowProc(HWND hwnd,
+ UINT msg,
+ WPARAM w_param,
+ LPARAM l_param) {
+ switch (msg) {
+ case WM_PAINT:
+ PAINTSTRUCT paint_struct;
+ HDC hdc = BeginPaint(hwnd, &paint_struct);
+
+ // Paint the window so the color is consistent and we can inspect the
+ // pixels in tests and know what to expect.
+ FillRect(hdc, &paint_struct.rcPaint,
+ CreateSolidBrush(RGB(kTestWindowRValue, kTestWindowGValue,
+ kTestWindowBValue)));
+
+ EndPaint(hwnd, &paint_struct);
+ }
+ return DefWindowProc(hwnd, msg, w_param, l_param);
+}
+
+} // namespace
+
+WindowInfo CreateTestWindow(const WCHAR* window_title,
+ const int height,
+ const int width,
+ const LONG extended_styles) {
+ WindowInfo info;
+ ::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<LPCWSTR>(&WindowProc),
+ &info.window_instance);
+
+ WNDCLASSEXW wcex;
+ memset(&wcex, 0, sizeof(wcex));
+ wcex.cbSize = sizeof(wcex);
+ wcex.style = CS_HREDRAW | CS_VREDRAW;
+ wcex.hInstance = info.window_instance;
+ wcex.lpfnWndProc = &WindowProc;
+ wcex.lpszClassName = kWindowClass;
+ info.window_class = ::RegisterClassExW(&wcex);
+
+ // Use the default height and width if the caller did not supply the optional
+ // height and width parameters, or if they supplied invalid values.
+ int window_height = height <= 0 ? kWindowHeight : height;
+ int window_width = width <= 0 ? kWindowWidth : width;
+ info.hwnd =
+ ::CreateWindowExW(extended_styles, kWindowClass, window_title,
+ WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
+ window_width, window_height, /*parent_window=*/nullptr,
+ /*menu_bar=*/nullptr, info.window_instance,
+ /*additional_params=*/nullptr);
+
+ ::ShowWindow(info.hwnd, SW_SHOWNORMAL);
+ ::UpdateWindow(info.hwnd);
+ return info;
+}
+
+void ResizeTestWindow(const HWND hwnd, const int width, const int height) {
+ // SWP_NOMOVE results in the x and y params being ignored.
+ ::SetWindowPos(hwnd, HWND_TOP, /*x-coord=*/0, /*y-coord=*/0, width, height,
+ SWP_SHOWWINDOW | SWP_NOMOVE);
+ ::UpdateWindow(hwnd);
+}
+
+void MoveTestWindow(const HWND hwnd, const int x, const int y) {
+ // SWP_NOSIZE results in the width and height params being ignored.
+ ::SetWindowPos(hwnd, HWND_TOP, x, y, /*width=*/0, /*height=*/0,
+ SWP_SHOWWINDOW | SWP_NOSIZE);
+ ::UpdateWindow(hwnd);
+}
+
+void MinimizeTestWindow(const HWND hwnd) {
+ ::ShowWindow(hwnd, SW_MINIMIZE);
+}
+
+void UnminimizeTestWindow(const HWND hwnd) {
+ ::OpenIcon(hwnd);
+}
+
+void DestroyTestWindow(WindowInfo info) {
+ ::DestroyWindow(info.hwnd);
+ ::UnregisterClass(MAKEINTATOM(info.window_class), info.window_instance);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.h b/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.h
new file mode 100644
index 0000000000..b055da7ccd
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 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 MODULES_DESKTOP_CAPTURE_WIN_TEST_SUPPORT_TEST_WINDOW_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_TEST_SUPPORT_TEST_WINDOW_H_
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+namespace webrtc {
+
+typedef unsigned char uint8_t;
+
+// Define an arbitrary color for the test window with unique R, G, and B values
+// so consumers can verify captured content in tests.
+const uint8_t kTestWindowRValue = 191;
+const uint8_t kTestWindowGValue = 99;
+const uint8_t kTestWindowBValue = 12;
+
+struct WindowInfo {
+ HWND hwnd;
+ HINSTANCE window_instance;
+ ATOM window_class;
+};
+
+WindowInfo CreateTestWindow(const WCHAR* window_title,
+ int height = 0,
+ int width = 0,
+ LONG extended_styles = 0);
+
+void ResizeTestWindow(HWND hwnd, int width, int height);
+
+void MoveTestWindow(HWND hwnd, int x, int y);
+
+void MinimizeTestWindow(HWND hwnd);
+
+void UnminimizeTestWindow(HWND hwnd);
+
+void DestroyTestWindow(WindowInfo info);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_TEST_SUPPORT_TEST_WINDOW_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.cc
new file mode 100644
index 0000000000..86afc52411
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.cc
@@ -0,0 +1,591 @@
+/*
+ * Copyright (c) 2020 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 "modules/desktop_capture/win/wgc_capture_session.h"
+
+#include <DispatcherQueue.h>
+#include <windows.graphics.capture.interop.h>
+#include <windows.graphics.directX.direct3d11.interop.h>
+#include <windows.graphics.h>
+#include <wrl/client.h>
+#include <wrl/event.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "modules/desktop_capture/win/wgc_desktop_frame.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/win/create_direct3d_device.h"
+#include "rtc_base/win/get_activation_factory.h"
+#include "system_wrappers/include/metrics.h"
+#include "system_wrappers/include/sleep.h"
+
+using Microsoft::WRL::ComPtr;
+namespace WGC = ABI::Windows::Graphics::Capture;
+
+namespace webrtc {
+namespace {
+
+// We must use a BGRA pixel format that has 4 bytes per pixel, as required by
+// the DesktopFrame interface.
+constexpr auto kPixelFormat = ABI::Windows::Graphics::DirectX::
+ DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized;
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class StartCaptureResult {
+ kSuccess = 0,
+ kSourceClosed = 1,
+ kAddClosedFailed = 2,
+ kDxgiDeviceCastFailed = 3,
+ kD3dDelayLoadFailed = 4,
+ kD3dDeviceCreationFailed = 5,
+ kFramePoolActivationFailed = 6,
+ // kFramePoolCastFailed = 7, (deprecated)
+ // kGetItemSizeFailed = 8, (deprecated)
+ kCreateFramePoolFailed = 9,
+ kCreateCaptureSessionFailed = 10,
+ kStartCaptureFailed = 11,
+ kMaxValue = kStartCaptureFailed
+};
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class GetFrameResult {
+ kSuccess = 0,
+ kItemClosed = 1,
+ kTryGetNextFrameFailed = 2,
+ kFrameDropped = 3,
+ kGetSurfaceFailed = 4,
+ kDxgiInterfaceAccessFailed = 5,
+ kTexture2dCastFailed = 6,
+ kCreateMappedTextureFailed = 7,
+ kMapFrameFailed = 8,
+ kGetContentSizeFailed = 9,
+ kResizeMappedTextureFailed = 10,
+ kRecreateFramePoolFailed = 11,
+ kFramePoolEmpty = 12,
+ kMaxValue = kFramePoolEmpty
+};
+
+void RecordStartCaptureResult(StartCaptureResult error) {
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult",
+ static_cast<int>(error), static_cast<int>(StartCaptureResult::kMaxValue));
+}
+
+void RecordGetFrameResult(GetFrameResult error) {
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.DesktopCapture.Win.WgcCaptureSessionGetFrameResult",
+ static_cast<int>(error), static_cast<int>(GetFrameResult::kMaxValue));
+}
+
+bool SizeHasChanged(ABI::Windows::Graphics::SizeInt32 size_new,
+ ABI::Windows::Graphics::SizeInt32 size_old) {
+ return (size_new.Height != size_old.Height ||
+ size_new.Width != size_old.Width);
+}
+
+} // namespace
+
+WgcCaptureSession::WgcCaptureSession(ComPtr<ID3D11Device> d3d11_device,
+ ComPtr<WGC::IGraphicsCaptureItem> item,
+ ABI::Windows::Graphics::SizeInt32 size)
+ : d3d11_device_(std::move(d3d11_device)),
+ item_(std::move(item)),
+ size_(size) {}
+
+WgcCaptureSession::~WgcCaptureSession() {
+ RemoveEventHandler();
+}
+
+HRESULT WgcCaptureSession::StartCapture(const DesktopCaptureOptions& options) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_DCHECK(!is_capture_started_);
+
+ if (item_closed_) {
+ RTC_LOG(LS_ERROR) << "The target source has been closed.";
+ RecordStartCaptureResult(StartCaptureResult::kSourceClosed);
+ return E_ABORT;
+ }
+
+ RTC_DCHECK(d3d11_device_);
+ RTC_DCHECK(item_);
+
+ // Listen for the Closed event, to detect if the source we are capturing is
+ // closed (e.g. application window is closed or monitor is disconnected). If
+ // it is, we should abort the capture.
+ item_closed_token_ = std::make_unique<EventRegistrationToken>();
+ auto closed_handler =
+ Microsoft::WRL::Callback<ABI::Windows::Foundation::ITypedEventHandler<
+ WGC::GraphicsCaptureItem*, IInspectable*>>(
+ this, &WgcCaptureSession::OnItemClosed);
+ HRESULT hr =
+ item_->add_Closed(closed_handler.Get(), item_closed_token_.get());
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kAddClosedFailed);
+ return hr;
+ }
+
+ ComPtr<IDXGIDevice> dxgi_device;
+ hr = d3d11_device_->QueryInterface(IID_PPV_ARGS(&dxgi_device));
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kDxgiDeviceCastFailed);
+ return hr;
+ }
+
+ if (!ResolveCoreWinRTDirect3DDelayload()) {
+ RecordStartCaptureResult(StartCaptureResult::kD3dDelayLoadFailed);
+ return E_FAIL;
+ }
+
+ hr = CreateDirect3DDeviceFromDXGIDevice(dxgi_device.Get(), &direct3d_device_);
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kD3dDeviceCreationFailed);
+ return hr;
+ }
+
+ ComPtr<WGC::IDirect3D11CaptureFramePoolStatics> frame_pool_statics;
+ hr = GetActivationFactory<
+ WGC::IDirect3D11CaptureFramePoolStatics,
+ RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool>(
+ &frame_pool_statics);
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kFramePoolActivationFailed);
+ return hr;
+ }
+
+ hr = frame_pool_statics->Create(direct3d_device_.Get(), kPixelFormat,
+ kNumBuffers, size_, &frame_pool_);
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kCreateFramePoolFailed);
+ return hr;
+ }
+
+ hr = frame_pool_->CreateCaptureSession(item_.Get(), &session_);
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kCreateCaptureSessionFailed);
+ return hr;
+ }
+
+ if (!options.prefer_cursor_embedded()) {
+ ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureSession2> session2;
+ if (SUCCEEDED(session_->QueryInterface(
+ ABI::Windows::Graphics::Capture::IID_IGraphicsCaptureSession2,
+ &session2))) {
+ session2->put_IsCursorCaptureEnabled(false);
+ }
+ }
+
+// Until Mozilla builds with Win 10 SDK v10.0.20348.0 or newer, this
+// code will not build. Once we support the newer SDK, Bug 1868198
+// exists to decide if we ever want to use this code since it is
+// removing an indicator that capture is happening.
+#if !defined(WEBRTC_MOZILLA_BUILD)
+ // By default, the WGC capture API adds a yellow border around the captured
+ // window or display to indicate that a capture is in progress. The section
+ // below is an attempt to remove this yellow border to make the capture
+ // experience more inline with the DXGI capture path.
+ // This requires 10.0.20348.0 or later, which practically means Windows 11.
+ ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureSession3> session3;
+ if (SUCCEEDED(session_->QueryInterface(
+ ABI::Windows::Graphics::Capture::IID_IGraphicsCaptureSession3,
+ &session3))) {
+ session3->put_IsBorderRequired(false);
+ }
+#endif
+
+ allow_zero_hertz_ = options.allow_wgc_zero_hertz();
+
+ hr = session_->StartCapture();
+ if (FAILED(hr)) {
+ RTC_LOG(LS_ERROR) << "Failed to start CaptureSession: " << hr;
+ RecordStartCaptureResult(StartCaptureResult::kStartCaptureFailed);
+ return hr;
+ }
+
+ RecordStartCaptureResult(StartCaptureResult::kSuccess);
+
+ is_capture_started_ = true;
+ return hr;
+}
+
+void WgcCaptureSession::EnsureFrame() {
+ // Try to process the captured frame and copy it to the `queue_`.
+ HRESULT hr = ProcessFrame();
+ if (SUCCEEDED(hr)) {
+ RTC_CHECK(queue_.current_frame());
+ return;
+ }
+
+ // We failed to process the frame, but we do have a frame so just return that.
+ if (queue_.current_frame()) {
+ RTC_LOG(LS_ERROR) << "ProcessFrame failed, using existing frame: " << hr;
+ return;
+ }
+
+ // ProcessFrame failed and we don't have a current frame. This could indicate
+ // a startup path where we may need to try/wait a few times to ensure that we
+ // have a frame. We try to get a new frame from the frame pool for a maximum
+ // of 10 times after sleeping for 20ms. We choose 20ms as it's just a bit
+ // longer than 17ms (for 60fps*) and hopefully avoids unlucky timing causing
+ // us to wait two frames when we mostly seem to only need to wait for one.
+ // This approach should ensure that GetFrame() always delivers a valid frame
+ // with a max latency of 200ms and often after sleeping only once.
+ // The scheme is heuristic and based on manual testing.
+ // (*) On a modern system, the FPS / monitor refresh rate is usually larger
+ // than or equal to 60.
+
+ const int max_sleep_count = 10;
+ const int sleep_time_ms = 20;
+
+ int sleep_count = 0;
+ while (!queue_.current_frame() && sleep_count < max_sleep_count) {
+ sleep_count++;
+ webrtc::SleepMs(sleep_time_ms);
+ hr = ProcessFrame();
+ if (FAILED(hr)) {
+ RTC_DLOG(LS_WARNING) << "ProcessFrame failed during startup: " << hr;
+ }
+ }
+ RTC_LOG_IF(LS_ERROR, !queue_.current_frame())
+ << "Unable to process a valid frame even after trying 10 times.";
+}
+
+bool WgcCaptureSession::GetFrame(std::unique_ptr<DesktopFrame>* output_frame,
+ bool source_should_be_capturable) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+
+ // Try to process the captured frame and wait some if needed. Avoid trying
+ // if we know that the source will not be capturable. This can happen e.g.
+ // when captured window is minimized and if EnsureFrame() was called in this
+ // state a large amount of kFrameDropped errors would be logged.
+ if (source_should_be_capturable)
+ EnsureFrame();
+
+ // Return a NULL frame and false as `result` if we still don't have a valid
+ // frame. This will lead to a DesktopCapturer::Result::ERROR_PERMANENT being
+ // posted by the WGC capturer.
+ DesktopFrame* current_frame = queue_.current_frame();
+ if (!current_frame) {
+ RTC_LOG(LS_ERROR) << "GetFrame failed.";
+ return false;
+ }
+
+ // Swap in the DesktopRegion in `damage_region_` which is updated in
+ // ProcessFrame(). The updated region is either empty or the full rect being
+ // captured where an empty damage region corresponds to "no change in content
+ // since last frame".
+ current_frame->mutable_updated_region()->Swap(&damage_region_);
+ damage_region_.Clear();
+
+ // Emit the current frame.
+ std::unique_ptr<DesktopFrame> new_frame = queue_.current_frame()->Share();
+ *output_frame = std::move(new_frame);
+
+ return true;
+}
+
+HRESULT WgcCaptureSession::CreateMappedTexture(
+ ComPtr<ID3D11Texture2D> src_texture,
+ UINT width,
+ UINT height) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+
+ D3D11_TEXTURE2D_DESC src_desc;
+ src_texture->GetDesc(&src_desc);
+ D3D11_TEXTURE2D_DESC map_desc;
+ map_desc.Width = width == 0 ? src_desc.Width : width;
+ map_desc.Height = height == 0 ? src_desc.Height : height;
+ map_desc.MipLevels = src_desc.MipLevels;
+ map_desc.ArraySize = src_desc.ArraySize;
+ map_desc.Format = src_desc.Format;
+ map_desc.SampleDesc = src_desc.SampleDesc;
+ map_desc.Usage = D3D11_USAGE_STAGING;
+ map_desc.BindFlags = 0;
+ map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ map_desc.MiscFlags = 0;
+ return d3d11_device_->CreateTexture2D(&map_desc, nullptr, &mapped_texture_);
+}
+
+HRESULT WgcCaptureSession::ProcessFrame() {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+
+ if (item_closed_) {
+ RTC_LOG(LS_ERROR) << "The target source has been closed.";
+ RecordGetFrameResult(GetFrameResult::kItemClosed);
+ return E_ABORT;
+ }
+
+ RTC_DCHECK(is_capture_started_);
+
+ ComPtr<WGC::IDirect3D11CaptureFrame> capture_frame;
+ HRESULT hr = frame_pool_->TryGetNextFrame(&capture_frame);
+ if (FAILED(hr)) {
+ RTC_LOG(LS_ERROR) << "TryGetNextFrame failed: " << hr;
+ RecordGetFrameResult(GetFrameResult::kTryGetNextFrameFailed);
+ return hr;
+ }
+
+ if (!capture_frame) {
+ if (!queue_.current_frame()) {
+ // The frame pool was empty and so is the external queue.
+ RTC_DLOG(LS_ERROR) << "Frame pool was empty => kFrameDropped.";
+ RecordGetFrameResult(GetFrameResult::kFrameDropped);
+ } else {
+ // The frame pool was empty but there is still one old frame available in
+ // external the queue.
+ RTC_DLOG(LS_WARNING) << "Frame pool was empty => kFramePoolEmpty.";
+ RecordGetFrameResult(GetFrameResult::kFramePoolEmpty);
+ }
+ return E_FAIL;
+ }
+
+ queue_.MoveToNextFrame();
+ if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
+ RTC_DLOG(LS_VERBOSE) << "Overwriting frame that is still shared.";
+ }
+
+ // We need to get `capture_frame` as an `ID3D11Texture2D` so that we can get
+ // the raw image data in the format required by the `DesktopFrame` interface.
+ ComPtr<ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface>
+ d3d_surface;
+ hr = capture_frame->get_Surface(&d3d_surface);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kGetSurfaceFailed);
+ return hr;
+ }
+
+ ComPtr<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>
+ direct3DDxgiInterfaceAccess;
+ hr = d3d_surface->QueryInterface(IID_PPV_ARGS(&direct3DDxgiInterfaceAccess));
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kDxgiInterfaceAccessFailed);
+ return hr;
+ }
+
+ ComPtr<ID3D11Texture2D> texture_2D;
+ hr = direct3DDxgiInterfaceAccess->GetInterface(IID_PPV_ARGS(&texture_2D));
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kTexture2dCastFailed);
+ return hr;
+ }
+
+ if (!mapped_texture_) {
+ hr = CreateMappedTexture(texture_2D);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kCreateMappedTextureFailed);
+ return hr;
+ }
+ }
+
+ // We need to copy `texture_2D` into `mapped_texture_` as the latter has the
+ // D3D11_CPU_ACCESS_READ flag set, which lets us access the image data.
+ // Otherwise it would only be readable by the GPU.
+ ComPtr<ID3D11DeviceContext> d3d_context;
+ d3d11_device_->GetImmediateContext(&d3d_context);
+
+ ABI::Windows::Graphics::SizeInt32 new_size;
+ hr = capture_frame->get_ContentSize(&new_size);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kGetContentSizeFailed);
+ return hr;
+ }
+
+ // If the size changed, we must resize `mapped_texture_` and `frame_pool_` to
+ // fit the new size. This must be done before `CopySubresourceRegion` so that
+ // the textures are the same size.
+ if (SizeHasChanged(new_size, size_)) {
+ hr = CreateMappedTexture(texture_2D, new_size.Width, new_size.Height);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kResizeMappedTextureFailed);
+ return hr;
+ }
+
+ hr = frame_pool_->Recreate(direct3d_device_.Get(), kPixelFormat,
+ kNumBuffers, new_size);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kRecreateFramePoolFailed);
+ return hr;
+ }
+ }
+
+ // If the size has changed since the last capture, we must be sure to use
+ // the smaller dimensions. Otherwise we might overrun our buffer, or
+ // read stale data from the last frame.
+ int image_height = std::min(size_.Height, new_size.Height);
+ int image_width = std::min(size_.Width, new_size.Width);
+
+ D3D11_BOX copy_region;
+ copy_region.left = 0;
+ copy_region.top = 0;
+ copy_region.right = image_width;
+ copy_region.bottom = image_height;
+ // Our textures are 2D so we just want one "slice" of the box.
+ copy_region.front = 0;
+ copy_region.back = 1;
+ d3d_context->CopySubresourceRegion(mapped_texture_.Get(),
+ /*dst_subresource_index=*/0, /*dst_x=*/0,
+ /*dst_y=*/0, /*dst_z=*/0, texture_2D.Get(),
+ /*src_subresource_index=*/0, &copy_region);
+
+ D3D11_MAPPED_SUBRESOURCE map_info;
+ hr = d3d_context->Map(mapped_texture_.Get(), /*subresource_index=*/0,
+ D3D11_MAP_READ, /*D3D11_MAP_FLAG_DO_NOT_WAIT=*/0,
+ &map_info);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kMapFrameFailed);
+ return hr;
+ }
+
+ // Allocate the current frame buffer only if it is not already allocated or
+ // if the size has changed. Note that we can't reallocate other buffers at
+ // this point, since the caller may still be reading from them. The queue can
+ // hold up to two frames.
+ DesktopSize image_size(image_width, image_height);
+ if (!queue_.current_frame() ||
+ !queue_.current_frame()->size().equals(image_size)) {
+ std::unique_ptr<DesktopFrame> buffer =
+ std::make_unique<BasicDesktopFrame>(image_size);
+ queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(buffer)));
+ }
+
+ DesktopFrame* current_frame = queue_.current_frame();
+ DesktopFrame* previous_frame = queue_.previous_frame();
+
+ // Will be set to true while copying the frame data to the `current_frame` if
+ // we can already determine that the content of the new frame differs from the
+ // previous. The idea is to get a low-complexity indication of if the content
+ // is static or not without performing a full/deep memory comparison when
+ // updating the damaged region.
+ bool frame_content_has_changed = false;
+
+ // Check if the queue contains two frames whose content can be compared.
+ const bool frame_content_can_be_compared = FrameContentCanBeCompared();
+
+ // Make a copy of the data pointed to by `map_info.pData` to the preallocated
+ // `current_frame` so we are free to unmap our texture. If possible, also
+ // perform a light-weight scan of the vertical line of pixels in the middle
+ // of the screen. A comparison is performed between two 32-bit pixels (RGBA);
+ // one from the current frame and one from the previous, and as soon as a
+ // difference is detected the scan stops and `frame_content_has_changed` is
+ // set to true.
+ uint8_t* src_data = static_cast<uint8_t*>(map_info.pData);
+ uint8_t* dst_data = current_frame->data();
+ uint8_t* prev_data =
+ frame_content_can_be_compared ? previous_frame->data() : nullptr;
+
+ const int width_in_bytes =
+ current_frame->size().width() * DesktopFrame::kBytesPerPixel;
+ RTC_DCHECK_GE(current_frame->stride(), width_in_bytes);
+ RTC_DCHECK_GE(map_info.RowPitch, width_in_bytes);
+ const int middle_pixel_offset =
+ (image_width / 2) * DesktopFrame::kBytesPerPixel;
+ for (int i = 0; i < image_height; i++) {
+ memcpy(dst_data, src_data, width_in_bytes);
+ if (prev_data && !frame_content_has_changed) {
+ uint8_t* previous_pixel = prev_data + middle_pixel_offset;
+ uint8_t* current_pixel = dst_data + middle_pixel_offset;
+ frame_content_has_changed =
+ memcmp(previous_pixel, current_pixel, DesktopFrame::kBytesPerPixel);
+ prev_data += current_frame->stride();
+ }
+ dst_data += current_frame->stride();
+ src_data += map_info.RowPitch;
+ }
+
+ d3d_context->Unmap(mapped_texture_.Get(), 0);
+
+ if (allow_zero_hertz()) {
+ if (previous_frame) {
+ const int previous_frame_size =
+ previous_frame->stride() * previous_frame->size().height();
+ const int current_frame_size =
+ current_frame->stride() * current_frame->size().height();
+
+ // Compare the latest frame with the previous and check if the frames are
+ // equal (both contain the exact same pixel values). Avoid full memory
+ // comparison if indication of a changed frame already exists from the
+ // stage above.
+ if (current_frame_size == previous_frame_size) {
+ if (frame_content_has_changed) {
+ // Mark frame as damaged based on existing light-weight indicator.
+ // Avoids deep memcmp of complete frame and saves resources.
+ damage_region_.SetRect(DesktopRect::MakeSize(current_frame->size()));
+ } else {
+ // Perform full memory comparison for all bytes between the current
+ // and the previous frames.
+ const bool frames_are_equal =
+ !memcmp(current_frame->data(), previous_frame->data(),
+ current_frame_size);
+ if (!frames_are_equal) {
+ // TODO(https://crbug.com/1421242): If we had an API to report
+ // proper damage regions we should be doing AddRect() with a
+ // SetRect() call on a resize.
+ damage_region_.SetRect(
+ DesktopRect::MakeSize(current_frame->size()));
+ }
+ }
+ } else {
+ // Mark resized frames as damaged.
+ damage_region_.SetRect(DesktopRect::MakeSize(current_frame->size()));
+ }
+ }
+ }
+
+ size_ = new_size;
+ RecordGetFrameResult(GetFrameResult::kSuccess);
+ return hr;
+}
+
+HRESULT WgcCaptureSession::OnItemClosed(WGC::IGraphicsCaptureItem* sender,
+ IInspectable* event_args) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+
+ RTC_LOG(LS_INFO) << "Capture target has been closed.";
+ item_closed_ = true;
+
+ RemoveEventHandler();
+
+ // Do not attempt to free resources in the OnItemClosed handler, as this
+ // causes a race where we try to delete the item that is calling us. Removing
+ // the event handlers and setting `item_closed_` above is sufficient to ensure
+ // that the resources are no longer used, and the next time the capturer tries
+ // to get a frame, we will report a permanent failure and be destroyed.
+ return S_OK;
+}
+
+void WgcCaptureSession::RemoveEventHandler() {
+ HRESULT hr;
+ if (item_ && item_closed_token_) {
+ hr = item_->remove_Closed(*item_closed_token_);
+ item_closed_token_.reset();
+ if (FAILED(hr))
+ RTC_LOG(LS_WARNING) << "Failed to remove Closed event handler: " << hr;
+ }
+}
+
+bool WgcCaptureSession::FrameContentCanBeCompared() {
+ DesktopFrame* current_frame = queue_.current_frame();
+ DesktopFrame* previous_frame = queue_.previous_frame();
+ if (!current_frame || !previous_frame) {
+ return false;
+ }
+ if (current_frame->stride() != previous_frame->stride()) {
+ return false;
+ }
+ return current_frame->size().equals(previous_frame->size());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.h b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.h
new file mode 100644
index 0000000000..d2901d9199
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2020 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 MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_
+
+#include <d3d11.h>
+#include <windows.graphics.capture.h>
+#include <windows.graphics.h>
+#include <wrl/client.h>
+
+#include <memory>
+
+#include "api/sequence_checker.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/win/wgc_capture_source.h"
+#include "rtc_base/event.h"
+
+namespace webrtc {
+
+class WgcCaptureSession final {
+ public:
+ WgcCaptureSession(
+ Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem> item,
+ ABI::Windows::Graphics::SizeInt32 size);
+
+ // Disallow copy and assign.
+ WgcCaptureSession(const WgcCaptureSession&) = delete;
+ WgcCaptureSession& operator=(const WgcCaptureSession&) = delete;
+
+ ~WgcCaptureSession();
+
+ HRESULT StartCapture(const DesktopCaptureOptions& options);
+
+ // Returns a frame from the local frame queue, if any are present.
+ bool GetFrame(std::unique_ptr<DesktopFrame>* output_frame,
+ bool source_should_be_capturable);
+
+ bool IsCaptureStarted() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return is_capture_started_;
+ }
+
+ // We keep 2 buffers in the frame pool since it results in a good compromise
+ // between latency/capture-rate and the rate at which
+ // Direct3D11CaptureFramePool.TryGetNextFrame returns NULL and we have to fall
+ // back to providing a copy from our external queue instead.
+ // We make this public for tests.
+ static constexpr int kNumBuffers = 2;
+
+ private:
+ // Initializes `mapped_texture_` with the properties of the `src_texture`,
+ // overrides the values of some necessary properties like the
+ // D3D11_CPU_ACCESS_READ flag. Also has optional parameters for what size
+ // `mapped_texture_` should be, if they aren't provided we will use the size
+ // of `src_texture`.
+ HRESULT CreateMappedTexture(
+ Microsoft::WRL::ComPtr<ID3D11Texture2D> src_texture,
+ UINT width = 0,
+ UINT height = 0);
+
+ // Event handler for `item_`'s Closed event.
+ HRESULT OnItemClosed(
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem* sender,
+ IInspectable* event_args);
+
+ // Wraps calls to ProcessFrame and deals with the uniqe start-up phase
+ // ensuring that we always have one captured frame available.
+ void EnsureFrame();
+
+ // Process the captured frame and copy it to the `queue_`.
+ HRESULT ProcessFrame();
+
+ void RemoveEventHandler();
+
+ bool FrameContentCanBeCompared();
+
+ bool allow_zero_hertz() const { return allow_zero_hertz_; }
+
+ std::unique_ptr<EventRegistrationToken> item_closed_token_;
+
+ // A Direct3D11 Device provided by the caller. We use this to create an
+ // IDirect3DDevice, and also to create textures that will hold the image data.
+ Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
+
+ // This item represents what we are capturing, we use it to create the
+ // capture session, and also to listen for the Closed event.
+ Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
+ item_;
+
+ // The IDirect3DDevice is necessary to instantiate the frame pool.
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>
+ direct3d_device_;
+
+ // The frame pool is where frames are deposited during capture, we retrieve
+ // them from here with TryGetNextFrame().
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool>
+ frame_pool_;
+
+ // This texture holds the final image data. We made it a member so we can
+ // reuse it, instead of having to create a new texture every time we grab a
+ // frame.
+ Microsoft::WRL::ComPtr<ID3D11Texture2D> mapped_texture_;
+
+ // This is the size of `mapped_texture_` and the buffers in `frame_pool_`. We
+ // store this as a member so we can compare it to the size of incoming frames
+ // and resize if necessary.
+ ABI::Windows::Graphics::SizeInt32 size_;
+
+ // The capture session lets us set properties about the capture before it
+ // starts such as whether to capture the mouse cursor, and it lets us tell WGC
+ // to start capturing frames.
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureSession>
+ session_;
+
+ // Queue of captured video frames. The queue holds 2 frames and it avoids
+ // alloc/dealloc per captured frame. Incoming frames from the internal frame
+ // pool are copied to this queue after required processing in ProcessFrame().
+ ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
+
+ bool item_closed_ = false;
+ bool is_capture_started_ = false;
+
+ // Caches the value of DesktopCaptureOptions.allow_wgc_zero_hertz() in
+ // StartCapture(). Adds 0Hz detection in ProcessFrame() when enabled which
+ // adds complexity since memcmp() is performed on two successive frames.
+ bool allow_zero_hertz_ = false;
+
+ // Tracks damage region updates that were reported since the last time a frame
+ // was captured. Currently only supports either the complete rect being
+ // captured or an empty region. Will always be empty if `allow_zero_hertz_` is
+ // false.
+ DesktopRegion damage_region_;
+
+ SequenceChecker sequence_checker_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.cc
new file mode 100644
index 0000000000..1688878cde
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.cc
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2020 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 "modules/desktop_capture/win/wgc_capture_source.h"
+
+#include <dwmapi.h>
+#include <windows.graphics.capture.interop.h>
+#include <windows.h>
+
+#include <utility>
+
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+#include "rtc_base/win/get_activation_factory.h"
+
+using Microsoft::WRL::ComPtr;
+namespace WGC = ABI::Windows::Graphics::Capture;
+
+namespace webrtc {
+
+WgcCaptureSource::WgcCaptureSource(DesktopCapturer::SourceId source_id)
+ : source_id_(source_id) {}
+WgcCaptureSource::~WgcCaptureSource() = default;
+
+bool WgcCaptureSource::ShouldBeCapturable() {
+ return true;
+}
+
+bool WgcCaptureSource::IsCapturable() {
+ // If we can create a capture item, then we can capture it. Unfortunately,
+ // we can't cache this item because it may be created in a different COM
+ // apartment than where capture will eventually start from.
+ ComPtr<WGC::IGraphicsCaptureItem> item;
+ return SUCCEEDED(CreateCaptureItem(&item));
+}
+
+bool WgcCaptureSource::FocusOnSource() {
+ return false;
+}
+
+ABI::Windows::Graphics::SizeInt32 WgcCaptureSource::GetSize() {
+ if (!item_)
+ return {0, 0};
+
+ ABI::Windows::Graphics::SizeInt32 item_size;
+ HRESULT hr = item_->get_Size(&item_size);
+ if (FAILED(hr))
+ return {0, 0};
+
+ return item_size;
+}
+
+HRESULT WgcCaptureSource::GetCaptureItem(
+ ComPtr<WGC::IGraphicsCaptureItem>* result) {
+ HRESULT hr = S_OK;
+ if (!item_)
+ hr = CreateCaptureItem(&item_);
+
+ *result = item_;
+ return hr;
+}
+
+WgcCaptureSourceFactory::~WgcCaptureSourceFactory() = default;
+
+WgcWindowSourceFactory::WgcWindowSourceFactory() = default;
+WgcWindowSourceFactory::~WgcWindowSourceFactory() = default;
+
+std::unique_ptr<WgcCaptureSource> WgcWindowSourceFactory::CreateCaptureSource(
+ DesktopCapturer::SourceId source_id) {
+ return std::make_unique<WgcWindowSource>(source_id);
+}
+
+WgcScreenSourceFactory::WgcScreenSourceFactory() = default;
+WgcScreenSourceFactory::~WgcScreenSourceFactory() = default;
+
+std::unique_ptr<WgcCaptureSource> WgcScreenSourceFactory::CreateCaptureSource(
+ DesktopCapturer::SourceId source_id) {
+ return std::make_unique<WgcScreenSource>(source_id);
+}
+
+WgcWindowSource::WgcWindowSource(DesktopCapturer::SourceId source_id)
+ : WgcCaptureSource(source_id) {}
+WgcWindowSource::~WgcWindowSource() = default;
+
+DesktopVector WgcWindowSource::GetTopLeft() {
+ DesktopRect window_rect;
+ if (!GetWindowRect(reinterpret_cast<HWND>(GetSourceId()), &window_rect))
+ return DesktopVector();
+
+ return window_rect.top_left();
+}
+
+ABI::Windows::Graphics::SizeInt32 WgcWindowSource::GetSize() {
+ RECT window_rect;
+ HRESULT hr = ::DwmGetWindowAttribute(
+ reinterpret_cast<HWND>(GetSourceId()), DWMWA_EXTENDED_FRAME_BOUNDS,
+ reinterpret_cast<void*>(&window_rect), sizeof(window_rect));
+ if (FAILED(hr))
+ return WgcCaptureSource::GetSize();
+
+ return {window_rect.right - window_rect.left,
+ window_rect.bottom - window_rect.top};
+}
+
+bool WgcWindowSource::ShouldBeCapturable() {
+ return IsWindowValidAndVisible(reinterpret_cast<HWND>(GetSourceId()));
+}
+
+bool WgcWindowSource::IsCapturable() {
+ if (!ShouldBeCapturable()) {
+ return false;
+ }
+
+ return WgcCaptureSource::IsCapturable();
+}
+
+bool WgcWindowSource::FocusOnSource() {
+ if (!IsWindowValidAndVisible(reinterpret_cast<HWND>(GetSourceId())))
+ return false;
+
+ return ::BringWindowToTop(reinterpret_cast<HWND>(GetSourceId())) &&
+ ::SetForegroundWindow(reinterpret_cast<HWND>(GetSourceId()));
+}
+
+HRESULT WgcWindowSource::CreateCaptureItem(
+ ComPtr<WGC::IGraphicsCaptureItem>* result) {
+ if (!ResolveCoreWinRTDelayload())
+ return E_FAIL;
+
+ ComPtr<IGraphicsCaptureItemInterop> interop;
+ HRESULT hr = GetActivationFactory<
+ IGraphicsCaptureItemInterop,
+ RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop);
+ if (FAILED(hr))
+ return hr;
+
+ ComPtr<WGC::IGraphicsCaptureItem> item;
+ hr = interop->CreateForWindow(reinterpret_cast<HWND>(GetSourceId()),
+ IID_PPV_ARGS(&item));
+ if (FAILED(hr))
+ return hr;
+
+ if (!item)
+ return E_HANDLE;
+
+ *result = std::move(item);
+ return hr;
+}
+
+WgcScreenSource::WgcScreenSource(DesktopCapturer::SourceId source_id)
+ : WgcCaptureSource(source_id) {
+ // Getting the HMONITOR could fail if the source_id is invalid. In that case,
+ // we leave hmonitor_ uninitialized and `IsCapturable()` will fail.
+ HMONITOR hmon;
+ if (GetHmonitorFromDeviceIndex(GetSourceId(), &hmon))
+ hmonitor_ = hmon;
+}
+
+WgcScreenSource::~WgcScreenSource() = default;
+
+DesktopVector WgcScreenSource::GetTopLeft() {
+ if (!hmonitor_)
+ return DesktopVector();
+
+ return GetMonitorRect(*hmonitor_).top_left();
+}
+
+ABI::Windows::Graphics::SizeInt32 WgcScreenSource::GetSize() {
+ ABI::Windows::Graphics::SizeInt32 size = WgcCaptureSource::GetSize();
+ if (!hmonitor_ || (size.Width != 0 && size.Height != 0))
+ return size;
+
+ DesktopRect rect = GetMonitorRect(*hmonitor_);
+ return {rect.width(), rect.height()};
+}
+
+bool WgcScreenSource::IsCapturable() {
+ if (!hmonitor_)
+ return false;
+
+ if (!IsMonitorValid(*hmonitor_))
+ return false;
+
+ return WgcCaptureSource::IsCapturable();
+}
+
+HRESULT WgcScreenSource::CreateCaptureItem(
+ ComPtr<WGC::IGraphicsCaptureItem>* result) {
+ if (!hmonitor_)
+ return E_ABORT;
+
+ if (!ResolveCoreWinRTDelayload())
+ return E_FAIL;
+
+ ComPtr<IGraphicsCaptureItemInterop> interop;
+ HRESULT hr = GetActivationFactory<
+ IGraphicsCaptureItemInterop,
+ RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop);
+ if (FAILED(hr))
+ return hr;
+
+ // Ensure the monitor is still valid (hasn't disconnected) before trying to
+ // create the item. On versions of Windows before Win11, `CreateForMonitor`
+ // will crash if no displays are connected.
+ if (!IsMonitorValid(hmonitor_.value()))
+ return E_ABORT;
+
+ ComPtr<WGC::IGraphicsCaptureItem> item;
+ hr = interop->CreateForMonitor(*hmonitor_, IID_PPV_ARGS(&item));
+ if (FAILED(hr))
+ return hr;
+
+ if (!item)
+ return E_HANDLE;
+
+ *result = std::move(item);
+ return hr;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.h b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.h
new file mode 100644
index 0000000000..57b105c2f8
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2020 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 MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_
+
+#include <windows.graphics.capture.h>
+#include <windows.graphics.h>
+#include <wrl/client.h>
+
+#include <memory>
+
+#include "absl/types/optional.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+// Abstract class to represent the source that WGC-based capturers capture
+// from. Could represent an application window or a screen. Consumers should use
+// the appropriate Wgc*SourceFactory class to create WgcCaptureSource objects
+// of the appropriate type.
+class WgcCaptureSource {
+ public:
+ explicit WgcCaptureSource(DesktopCapturer::SourceId source_id);
+ virtual ~WgcCaptureSource();
+
+ virtual DesktopVector GetTopLeft() = 0;
+ // Lightweight version of IsCapturable which avoids allocating/deallocating
+ // COM objects for each call. As such may return a different value than
+ // IsCapturable.
+ virtual bool ShouldBeCapturable();
+ virtual bool IsCapturable();
+ virtual bool FocusOnSource();
+ virtual ABI::Windows::Graphics::SizeInt32 GetSize();
+ HRESULT GetCaptureItem(
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result);
+ DesktopCapturer::SourceId GetSourceId() { return source_id_; }
+
+ protected:
+ virtual HRESULT CreateCaptureItem(
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result) = 0;
+
+ private:
+ Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
+ item_;
+ const DesktopCapturer::SourceId source_id_;
+};
+
+class WgcCaptureSourceFactory {
+ public:
+ virtual ~WgcCaptureSourceFactory();
+
+ virtual std::unique_ptr<WgcCaptureSource> CreateCaptureSource(
+ DesktopCapturer::SourceId) = 0;
+};
+
+class WgcWindowSourceFactory final : public WgcCaptureSourceFactory {
+ public:
+ WgcWindowSourceFactory();
+
+ // Disallow copy and assign.
+ WgcWindowSourceFactory(const WgcWindowSourceFactory&) = delete;
+ WgcWindowSourceFactory& operator=(const WgcWindowSourceFactory&) = delete;
+
+ ~WgcWindowSourceFactory() override;
+
+ std::unique_ptr<WgcCaptureSource> CreateCaptureSource(
+ DesktopCapturer::SourceId) override;
+};
+
+class WgcScreenSourceFactory final : public WgcCaptureSourceFactory {
+ public:
+ WgcScreenSourceFactory();
+
+ WgcScreenSourceFactory(const WgcScreenSourceFactory&) = delete;
+ WgcScreenSourceFactory& operator=(const WgcScreenSourceFactory&) = delete;
+
+ ~WgcScreenSourceFactory() override;
+
+ std::unique_ptr<WgcCaptureSource> CreateCaptureSource(
+ DesktopCapturer::SourceId) override;
+};
+
+// Class for capturing application windows.
+class WgcWindowSource final : public WgcCaptureSource {
+ public:
+ explicit WgcWindowSource(DesktopCapturer::SourceId source_id);
+
+ WgcWindowSource(const WgcWindowSource&) = delete;
+ WgcWindowSource& operator=(const WgcWindowSource&) = delete;
+
+ ~WgcWindowSource() override;
+
+ DesktopVector GetTopLeft() override;
+ ABI::Windows::Graphics::SizeInt32 GetSize() override;
+ bool ShouldBeCapturable() override;
+ bool IsCapturable() override;
+ bool FocusOnSource() override;
+
+ private:
+ HRESULT CreateCaptureItem(
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result)
+ override;
+};
+
+// Class for capturing screens/monitors/displays.
+class WgcScreenSource final : public WgcCaptureSource {
+ public:
+ explicit WgcScreenSource(DesktopCapturer::SourceId source_id);
+
+ WgcScreenSource(const WgcScreenSource&) = delete;
+ WgcScreenSource& operator=(const WgcScreenSource&) = delete;
+
+ ~WgcScreenSource() override;
+
+ DesktopVector GetTopLeft() override;
+ ABI::Windows::Graphics::SizeInt32 GetSize() override;
+ bool IsCapturable() override;
+
+ private:
+ HRESULT CreateCaptureItem(
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result)
+ override;
+
+ // To maintain compatibility with other capturers, this class accepts a
+ // device index as it's SourceId. However, WGC requires we use an HMONITOR to
+ // describe which screen to capture. So, we internally convert the supplied
+ // device index into an HMONITOR when `IsCapturable()` is called.
+ absl::optional<HMONITOR> hmonitor_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source_unittest.cc
new file mode 100644
index 0000000000..dc37ec2e0d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source_unittest.cc
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2021 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 "modules/desktop_capture/win/wgc_capture_source.h"
+
+#include <windows.graphics.capture.h>
+#include <wrl/client.h>
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/test_support/test_window.h"
+#include "modules/desktop_capture/win/wgc_capturer_win.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/win/scoped_com_initializer.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+const WCHAR kWindowTitle[] = L"WGC Capture Source Test Window";
+
+const int kFirstXCoord = 25;
+const int kFirstYCoord = 50;
+const int kSecondXCoord = 50;
+const int kSecondYCoord = 75;
+
+} // namespace
+
+class WgcCaptureSourceTest : public ::testing::TestWithParam<CaptureType> {
+ public:
+ void SetUp() override {
+ com_initializer_ =
+ std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
+ ASSERT_TRUE(com_initializer_->Succeeded());
+ }
+
+ void TearDown() override {
+ if (window_open_) {
+ DestroyTestWindow(window_info_);
+ }
+ }
+
+ void SetUpForWindowSource() {
+ window_info_ = CreateTestWindow(kWindowTitle);
+ window_open_ = true;
+ source_id_ = reinterpret_cast<DesktopCapturer::SourceId>(window_info_.hwnd);
+ source_factory_ = std::make_unique<WgcWindowSourceFactory>();
+ }
+
+ void SetUpForScreenSource() {
+ source_id_ = kFullDesktopScreenId;
+ source_factory_ = std::make_unique<WgcScreenSourceFactory>();
+ }
+
+ protected:
+ std::unique_ptr<ScopedCOMInitializer> com_initializer_;
+ std::unique_ptr<WgcCaptureSourceFactory> source_factory_;
+ std::unique_ptr<WgcCaptureSource> source_;
+ DesktopCapturer::SourceId source_id_;
+ WindowInfo window_info_;
+ bool window_open_ = false;
+};
+
+// Window specific test
+TEST_F(WgcCaptureSourceTest, WindowPosition) {
+ if (!IsWgcSupported(CaptureType::kWindow)) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+
+ SetUpForWindowSource();
+ source_ = source_factory_->CreateCaptureSource(source_id_);
+ ASSERT_TRUE(source_);
+ EXPECT_EQ(source_->GetSourceId(), source_id_);
+
+ MoveTestWindow(window_info_.hwnd, kFirstXCoord, kFirstYCoord);
+ DesktopVector source_vector = source_->GetTopLeft();
+ EXPECT_EQ(source_vector.x(), kFirstXCoord);
+ EXPECT_EQ(source_vector.y(), kFirstYCoord);
+
+ MoveTestWindow(window_info_.hwnd, kSecondXCoord, kSecondYCoord);
+ source_vector = source_->GetTopLeft();
+ EXPECT_EQ(source_vector.x(), kSecondXCoord);
+ EXPECT_EQ(source_vector.y(), kSecondYCoord);
+}
+
+// Screen specific test
+TEST_F(WgcCaptureSourceTest, ScreenPosition) {
+ if (!IsWgcSupported(CaptureType::kScreen)) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+
+ SetUpForScreenSource();
+ source_ = source_factory_->CreateCaptureSource(source_id_);
+ ASSERT_TRUE(source_);
+ EXPECT_EQ(source_id_, source_->GetSourceId());
+
+ DesktopRect screen_rect = GetFullscreenRect();
+ DesktopVector source_vector = source_->GetTopLeft();
+ EXPECT_EQ(source_vector.x(), screen_rect.left());
+ EXPECT_EQ(source_vector.y(), screen_rect.top());
+}
+
+// Source agnostic test
+TEST_P(WgcCaptureSourceTest, CreateSource) {
+ if (!IsWgcSupported(GetParam())) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+
+ if (GetParam() == CaptureType::kWindow) {
+ SetUpForWindowSource();
+ } else {
+ SetUpForScreenSource();
+ }
+
+ source_ = source_factory_->CreateCaptureSource(source_id_);
+ ASSERT_TRUE(source_);
+ EXPECT_EQ(source_id_, source_->GetSourceId());
+ EXPECT_TRUE(source_->IsCapturable());
+
+ Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
+ item;
+ EXPECT_TRUE(SUCCEEDED(source_->GetCaptureItem(&item)));
+ EXPECT_TRUE(item);
+}
+
+INSTANTIATE_TEST_SUITE_P(SourceAgnostic,
+ WgcCaptureSourceTest,
+ ::testing::Values(CaptureType::kWindow,
+ CaptureType::kScreen));
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc
new file mode 100644
index 0000000000..9c545597aa
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2020 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 "modules/desktop_capture/win/wgc_capturer_win.h"
+
+#include <DispatcherQueue.h>
+#include <windows.foundation.metadata.h>
+#include <windows.graphics.capture.h>
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/win/wgc_desktop_frame.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/win/get_activation_factory.h"
+#include "rtc_base/win/hstring.h"
+#include "rtc_base/win/windows_version.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace WGC = ABI::Windows::Graphics::Capture;
+using Microsoft::WRL::ComPtr;
+
+namespace webrtc {
+
+namespace {
+
+constexpr wchar_t kCoreMessagingDll[] = L"CoreMessaging.dll";
+
+constexpr wchar_t kWgcSessionType[] =
+ L"Windows.Graphics.Capture.GraphicsCaptureSession";
+constexpr wchar_t kApiContract[] = L"Windows.Foundation.UniversalApiContract";
+constexpr UINT16 kRequiredApiContractVersion = 8;
+
+enum class WgcCapturerResult {
+ kSuccess = 0,
+ kNoDirect3dDevice = 1,
+ kNoSourceSelected = 2,
+ kItemCreationFailure = 3,
+ kSessionStartFailure = 4,
+ kGetFrameFailure = 5,
+ kFrameDropped = 6,
+ kCreateDispatcherQueueFailure = 7,
+ kMaxValue = kCreateDispatcherQueueFailure
+};
+
+void RecordWgcCapturerResult(WgcCapturerResult error) {
+ RTC_HISTOGRAM_ENUMERATION("WebRTC.DesktopCapture.Win.WgcCapturerResult",
+ static_cast<int>(error),
+ static_cast<int>(WgcCapturerResult::kMaxValue));
+}
+
+} // namespace
+
+bool IsWgcSupported(CaptureType capture_type) {
+ if (!HasActiveDisplay()) {
+ // There is a bug in `CreateForMonitor` that causes a crash if there are no
+ // active displays. The crash was fixed in Win11, but we are still unable
+ // to capture screens without an active display.
+ if (capture_type == CaptureType::kScreen)
+ return false;
+
+ // There is a bug in the DWM (Desktop Window Manager) that prevents it from
+ // providing image data if there are no displays attached. This was fixed in
+ // Windows 11.
+ if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN11)
+ return false;
+ }
+
+ // A bug in the WGC API `CreateForMonitor` prevents capturing the entire
+ // virtual screen (all monitors simultaneously), this was fixed in 20H1. Since
+ // we can't assert that we won't be asked to capture the entire virtual
+ // screen, we report unsupported so we can fallback to another capturer.
+ if (capture_type == CaptureType::kScreen &&
+ rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_20H1) {
+ return false;
+ }
+
+ if (!ResolveCoreWinRTDelayload())
+ return false;
+
+ // We need to check if the WGC APIs are presesnt on the system. Certain SKUs
+ // of Windows ship without these APIs.
+ ComPtr<ABI::Windows::Foundation::Metadata::IApiInformationStatics>
+ api_info_statics;
+ HRESULT hr = GetActivationFactory<
+ ABI::Windows::Foundation::Metadata::IApiInformationStatics,
+ RuntimeClass_Windows_Foundation_Metadata_ApiInformation>(
+ &api_info_statics);
+ if (FAILED(hr))
+ return false;
+
+ HSTRING api_contract;
+ hr = webrtc::CreateHstring(kApiContract, wcslen(kApiContract), &api_contract);
+ if (FAILED(hr))
+ return false;
+
+ boolean is_api_present;
+ hr = api_info_statics->IsApiContractPresentByMajor(
+ api_contract, kRequiredApiContractVersion, &is_api_present);
+ webrtc::DeleteHstring(api_contract);
+ if (FAILED(hr) || !is_api_present)
+ return false;
+
+ HSTRING wgc_session_type;
+ hr = webrtc::CreateHstring(kWgcSessionType, wcslen(kWgcSessionType),
+ &wgc_session_type);
+ if (FAILED(hr))
+ return false;
+
+ boolean is_type_present;
+ hr = api_info_statics->IsTypePresent(wgc_session_type, &is_type_present);
+ webrtc::DeleteHstring(wgc_session_type);
+ if (FAILED(hr) || !is_type_present)
+ return false;
+
+ // If the APIs are present, we need to check that they are supported.
+ ComPtr<WGC::IGraphicsCaptureSessionStatics> capture_session_statics;
+ hr = GetActivationFactory<
+ WGC::IGraphicsCaptureSessionStatics,
+ RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureSession>(
+ &capture_session_statics);
+ if (FAILED(hr))
+ return false;
+
+ boolean is_supported;
+ hr = capture_session_statics->IsSupported(&is_supported);
+ if (FAILED(hr) || !is_supported)
+ return false;
+
+ return true;
+}
+
+WgcCapturerWin::WgcCapturerWin(
+ const DesktopCaptureOptions& options,
+ std::unique_ptr<WgcCaptureSourceFactory> source_factory,
+ std::unique_ptr<SourceEnumerator> source_enumerator,
+ bool allow_delayed_capturable_check)
+ : options_(options),
+ source_factory_(std::move(source_factory)),
+ source_enumerator_(std::move(source_enumerator)),
+ allow_delayed_capturable_check_(allow_delayed_capturable_check) {
+ if (!core_messaging_library_)
+ core_messaging_library_ = LoadLibraryW(kCoreMessagingDll);
+
+ if (core_messaging_library_) {
+ create_dispatcher_queue_controller_func_ =
+ reinterpret_cast<CreateDispatcherQueueControllerFunc>(GetProcAddress(
+ core_messaging_library_, "CreateDispatcherQueueController"));
+ }
+}
+
+WgcCapturerWin::~WgcCapturerWin() {
+ if (core_messaging_library_)
+ FreeLibrary(core_messaging_library_);
+}
+
+// static
+std::unique_ptr<DesktopCapturer> WgcCapturerWin::CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options,
+ bool allow_delayed_capturable_check) {
+ return std::make_unique<WgcCapturerWin>(
+ options, std::make_unique<WgcWindowSourceFactory>(),
+ std::make_unique<WindowEnumerator>(
+ options.enumerate_current_process_windows()),
+ allow_delayed_capturable_check);
+}
+
+// static
+std::unique_ptr<DesktopCapturer> WgcCapturerWin::CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options) {
+ return std::make_unique<WgcCapturerWin>(
+ options, std::make_unique<WgcScreenSourceFactory>(),
+ std::make_unique<ScreenEnumerator>(), false);
+}
+
+bool WgcCapturerWin::GetSourceList(SourceList* sources) {
+ return source_enumerator_->FindAllSources(sources);
+}
+
+bool WgcCapturerWin::SelectSource(DesktopCapturer::SourceId id) {
+ capture_source_ = source_factory_->CreateCaptureSource(id);
+ if (allow_delayed_capturable_check_)
+ return true;
+
+ return capture_source_->IsCapturable();
+}
+
+bool WgcCapturerWin::FocusOnSelectedSource() {
+ if (!capture_source_)
+ return false;
+
+ return capture_source_->FocusOnSource();
+}
+
+void WgcCapturerWin::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ RecordCapturerImpl(DesktopCapturerId::kWgcCapturerWin);
+
+ callback_ = callback;
+
+ // Create a Direct3D11 device to share amongst the WgcCaptureSessions. Many
+ // parameters are nullptr as the implemention uses defaults that work well for
+ // us.
+ HRESULT hr = D3D11CreateDevice(
+ /*adapter=*/nullptr, D3D_DRIVER_TYPE_HARDWARE,
+ /*software_rasterizer=*/nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
+ /*feature_levels=*/nullptr, /*feature_levels_size=*/0, D3D11_SDK_VERSION,
+ &d3d11_device_, /*feature_level=*/nullptr, /*device_context=*/nullptr);
+ if (hr == DXGI_ERROR_UNSUPPORTED) {
+ // If a hardware device could not be created, use WARP which is a high speed
+ // software device.
+ hr = D3D11CreateDevice(
+ /*adapter=*/nullptr, D3D_DRIVER_TYPE_WARP,
+ /*software_rasterizer=*/nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
+ /*feature_levels=*/nullptr, /*feature_levels_size=*/0,
+ D3D11_SDK_VERSION, &d3d11_device_, /*feature_level=*/nullptr,
+ /*device_context=*/nullptr);
+ }
+
+ if (FAILED(hr)) {
+ RTC_LOG(LS_ERROR) << "Failed to create D3D11Device: " << hr;
+ }
+}
+
+void WgcCapturerWin::CaptureFrame() {
+ RTC_DCHECK(callback_);
+
+ if (!capture_source_) {
+ RTC_LOG(LS_ERROR) << "Source hasn't been selected";
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kNoSourceSelected);
+ return;
+ }
+
+ if (!d3d11_device_) {
+ RTC_LOG(LS_ERROR) << "No D3D11D3evice, cannot capture.";
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kNoDirect3dDevice);
+ return;
+ }
+
+ if (allow_delayed_capturable_check_ && !capture_source_->IsCapturable()) {
+ RTC_LOG(LS_ERROR) << "Source is not capturable.";
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ return;
+ }
+
+ HRESULT hr;
+ if (!dispatcher_queue_created_) {
+ // Set the apartment type to NONE because this thread should already be COM
+ // initialized.
+ DispatcherQueueOptions options{
+ sizeof(DispatcherQueueOptions),
+ DISPATCHERQUEUE_THREAD_TYPE::DQTYPE_THREAD_CURRENT,
+ DISPATCHERQUEUE_THREAD_APARTMENTTYPE::DQTAT_COM_NONE};
+ ComPtr<ABI::Windows::System::IDispatcherQueueController> queue_controller;
+ hr = create_dispatcher_queue_controller_func_(options, &queue_controller);
+
+ // If there is already a DispatcherQueue on this thread, that is fine. Its
+ // lifetime is tied to the thread's, and as long as the thread has one, even
+ // if we didn't create it, the capture session's events will be delivered on
+ // this thread.
+ if (FAILED(hr) && hr != RPC_E_WRONG_THREAD) {
+ RecordWgcCapturerResult(WgcCapturerResult::kCreateDispatcherQueueFailure);
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ } else {
+ dispatcher_queue_created_ = true;
+ }
+ }
+
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ WgcCaptureSession* capture_session = nullptr;
+ std::map<SourceId, WgcCaptureSession>::iterator session_iter =
+ ongoing_captures_.find(capture_source_->GetSourceId());
+ if (session_iter == ongoing_captures_.end()) {
+ ComPtr<WGC::IGraphicsCaptureItem> item;
+ hr = capture_source_->GetCaptureItem(&item);
+ if (FAILED(hr)) {
+ RTC_LOG(LS_ERROR) << "Failed to create a GraphicsCaptureItem: " << hr;
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kItemCreationFailure);
+ return;
+ }
+
+ std::pair<std::map<SourceId, WgcCaptureSession>::iterator, bool>
+ iter_success_pair = ongoing_captures_.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(capture_source_->GetSourceId()),
+ std::forward_as_tuple(d3d11_device_, item,
+ capture_source_->GetSize()));
+ RTC_DCHECK(iter_success_pair.second);
+ capture_session = &iter_success_pair.first->second;
+ } else {
+ capture_session = &session_iter->second;
+ }
+
+ if (!capture_session->IsCaptureStarted()) {
+ hr = capture_session->StartCapture(options_);
+ if (FAILED(hr)) {
+ RTC_LOG(LS_ERROR) << "Failed to start capture: " << hr;
+ ongoing_captures_.erase(capture_source_->GetSourceId());
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kSessionStartFailure);
+ return;
+ }
+ }
+
+ std::unique_ptr<DesktopFrame> frame;
+ if (!capture_session->GetFrame(&frame,
+ capture_source_->ShouldBeCapturable())) {
+ RTC_LOG(LS_ERROR) << "GetFrame failed.";
+ ongoing_captures_.erase(capture_source_->GetSourceId());
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kGetFrameFailure);
+ return;
+ }
+
+ if (!frame) {
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kFrameDropped);
+ return;
+ }
+
+ int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec;
+ RTC_HISTOGRAM_COUNTS_1000("WebRTC.DesktopCapture.Win.WgcCapturerFrameTime",
+ capture_time_ms);
+ frame->set_capture_time_ms(capture_time_ms);
+ frame->set_capturer_id(DesktopCapturerId::kWgcCapturerWin);
+ frame->set_may_contain_cursor(options_.prefer_cursor_embedded());
+ frame->set_top_left(capture_source_->GetTopLeft());
+ RecordWgcCapturerResult(WgcCapturerResult::kSuccess);
+ callback_->OnCaptureResult(DesktopCapturer::Result::SUCCESS,
+ std::move(frame));
+}
+
+bool WgcCapturerWin::IsSourceBeingCaptured(DesktopCapturer::SourceId id) {
+ std::map<DesktopCapturer::SourceId, WgcCaptureSession>::iterator
+ session_iter = ongoing_captures_.find(id);
+ if (session_iter == ongoing_captures_.end())
+ return false;
+
+ return session_iter->second.IsCaptureStarted();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h
new file mode 100644
index 0000000000..30253d9db6
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2020 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 MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_
+
+#include <DispatcherQueue.h>
+#include <d3d11.h>
+#include <wrl/client.h>
+
+#include <map>
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/wgc_capture_session.h"
+#include "modules/desktop_capture/win/wgc_capture_source.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+
+namespace webrtc {
+
+// Checks if the WGC API is present and supported on the system.
+bool IsWgcSupported(CaptureType capture_type);
+
+// WgcCapturerWin is initialized with an implementation of this base class,
+// which it uses to find capturable sources of a particular type. This way,
+// WgcCapturerWin can remain source-agnostic.
+class SourceEnumerator {
+ public:
+ virtual ~SourceEnumerator() = default;
+
+ virtual bool FindAllSources(DesktopCapturer::SourceList* sources) = 0;
+};
+
+class WindowEnumerator final : public SourceEnumerator {
+ public:
+ explicit WindowEnumerator(bool enumerate_current_process_windows)
+ : enumerate_current_process_windows_(enumerate_current_process_windows) {}
+
+ WindowEnumerator(const WindowEnumerator&) = delete;
+ WindowEnumerator& operator=(const WindowEnumerator&) = delete;
+
+ ~WindowEnumerator() override = default;
+
+ bool FindAllSources(DesktopCapturer::SourceList* sources) override {
+ // WGC fails to capture windows with the WS_EX_TOOLWINDOW style, so we
+ // provide it as a filter to ensure windows with the style are not returned.
+ return window_capture_helper_.EnumerateCapturableWindows(
+ sources, enumerate_current_process_windows_, WS_EX_TOOLWINDOW);
+ }
+
+ private:
+ WindowCaptureHelperWin window_capture_helper_;
+ bool enumerate_current_process_windows_;
+};
+
+class ScreenEnumerator final : public SourceEnumerator {
+ public:
+ ScreenEnumerator() = default;
+
+ ScreenEnumerator(const ScreenEnumerator&) = delete;
+ ScreenEnumerator& operator=(const ScreenEnumerator&) = delete;
+
+ ~ScreenEnumerator() override = default;
+
+ bool FindAllSources(DesktopCapturer::SourceList* sources) override {
+ return webrtc::GetScreenList(sources);
+ }
+};
+
+// A capturer that uses the Window.Graphics.Capture APIs. It is suitable for
+// both window and screen capture (but only one type per instance). Consumers
+// should not instantiate this class directly, instead they should use
+// `CreateRawWindowCapturer()` or `CreateRawScreenCapturer()` to receive a
+// capturer appropriate for the type of source they want to capture.
+class WgcCapturerWin : public DesktopCapturer {
+ public:
+ WgcCapturerWin(const DesktopCaptureOptions& options,
+ std::unique_ptr<WgcCaptureSourceFactory> source_factory,
+ std::unique_ptr<SourceEnumerator> source_enumerator,
+ bool allow_delayed_capturable_check);
+
+ WgcCapturerWin(const WgcCapturerWin&) = delete;
+ WgcCapturerWin& operator=(const WgcCapturerWin&) = delete;
+
+ ~WgcCapturerWin() override;
+
+ static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options,
+ bool allow_delayed_capturable_check = false);
+
+ static std::unique_ptr<DesktopCapturer> CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options);
+
+ // DesktopCapturer interface.
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+
+ // Used in WgcCapturerTests.
+ bool IsSourceBeingCaptured(SourceId id);
+
+ private:
+ typedef HRESULT(WINAPI* CreateDispatcherQueueControllerFunc)(
+ DispatcherQueueOptions,
+ ABI::Windows::System::IDispatcherQueueController**);
+
+ DesktopCaptureOptions options_;
+
+ // We need to either create or ensure that someone else created a
+ // `DispatcherQueue` on the current thread so that events will be delivered
+ // on the current thread rather than an arbitrary thread. A
+ // `DispatcherQueue`'s lifetime is tied to the thread's, and we don't post
+ // any work to it, so we don't need to hold a reference.
+ bool dispatcher_queue_created_ = false;
+
+ // Statically linking to CoreMessaging.lib is disallowed in Chromium, so we
+ // load it at runtime.
+ HMODULE core_messaging_library_ = NULL;
+ CreateDispatcherQueueControllerFunc create_dispatcher_queue_controller_func_ =
+ nullptr;
+
+ // Factory to create a WgcCaptureSource for us whenever SelectSource is
+ // called. Initialized at construction with a source-specific implementation.
+ std::unique_ptr<WgcCaptureSourceFactory> source_factory_;
+
+ // The source enumerator helps us find capturable sources of the appropriate
+ // type. Initialized at construction with a source-specific implementation.
+ std::unique_ptr<SourceEnumerator> source_enumerator_;
+
+ // The WgcCaptureSource represents the source we are capturing. It tells us
+ // if the source is capturable and it creates the GraphicsCaptureItem for us.
+ std::unique_ptr<WgcCaptureSource> capture_source_;
+
+ // A map of all the sources we are capturing and the associated
+ // WgcCaptureSession. Frames for the current source (indicated via
+ // SelectSource) will be retrieved from the appropriate session when
+ // requested via CaptureFrame.
+ // This helps us efficiently capture multiple sources (e.g. when consumers
+ // are trying to display a list of available capture targets with thumbnails).
+ std::map<SourceId, WgcCaptureSession> ongoing_captures_;
+
+ // The callback that we deliver frames to, synchronously, before CaptureFrame
+ // returns.
+ Callback* callback_ = nullptr;
+
+ // WgcCaptureSource::IsCapturable is expensive to run. So, caller can
+ // delay capturable check till capture frame is called if the WgcCapturerWin
+ // is used as a fallback capturer.
+ bool allow_delayed_capturable_check_ = false;
+
+ // A Direct3D11 device that is shared amongst the WgcCaptureSessions, who
+ // require one to perform the capture.
+ Microsoft::WRL::ComPtr<::ID3D11Device> d3d11_device_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win_unittest.cc
new file mode 100644
index 0000000000..a7b656fcfc
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win_unittest.cc
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2020 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 "modules/desktop_capture/win/wgc_capturer_win.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/win/test_support/test_window.h"
+#include "modules/desktop_capture/win/wgc_capture_session.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "rtc_base/thread.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/win/scoped_com_initializer.h"
+#include "rtc_base/win/windows_version.h"
+#include "system_wrappers/include/metrics.h"
+#include "system_wrappers/include/sleep.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+constexpr char kWindowThreadName[] = "wgc_capturer_test_window_thread";
+constexpr WCHAR kWindowTitle[] = L"WGC Capturer Test Window";
+
+constexpr char kCapturerImplHistogram[] =
+ "WebRTC.DesktopCapture.Win.DesktopCapturerImpl";
+
+constexpr char kCapturerResultHistogram[] =
+ "WebRTC.DesktopCapture.Win.WgcCapturerResult";
+constexpr int kSuccess = 0;
+constexpr int kSessionStartFailure = 4;
+
+constexpr char kCaptureSessionResultHistogram[] =
+ "WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult";
+constexpr int kSourceClosed = 1;
+
+constexpr char kCaptureTimeHistogram[] =
+ "WebRTC.DesktopCapture.Win.WgcCapturerFrameTime";
+
+// The capturer keeps `kNumBuffers` in its frame pool, so we need to request
+// that many frames to clear those out. The next frame will have the new size
+// (if the size has changed) so we will resize the frame pool at this point.
+// Then, we need to clear any frames that may have delivered to the frame pool
+// before the resize. Finally, the next frame will be guaranteed to be the new
+// size.
+constexpr int kNumCapturesToFlushBuffers =
+ WgcCaptureSession::kNumBuffers * 2 + 1;
+
+constexpr int kSmallWindowWidth = 200;
+constexpr int kSmallWindowHeight = 100;
+constexpr int kMediumWindowWidth = 300;
+constexpr int kMediumWindowHeight = 200;
+constexpr int kLargeWindowWidth = 400;
+constexpr int kLargeWindowHeight = 500;
+
+// The size of the image we capture is slightly smaller than the actual size of
+// the window.
+constexpr int kWindowWidthSubtrahend = 14;
+constexpr int kWindowHeightSubtrahend = 7;
+
+// Custom message constants so we can direct our thread to close windows and
+// quit running.
+constexpr UINT kDestroyWindow = WM_APP;
+constexpr UINT kQuitRunning = WM_APP + 1;
+
+// When testing changes to real windows, sometimes the effects (close or resize)
+// don't happen immediately, we want to keep trying until we see the effect but
+// only for a reasonable amount of time.
+constexpr int kMaxTries = 50;
+
+} // namespace
+
+class WgcCapturerWinTest : public ::testing::TestWithParam<CaptureType>,
+ public DesktopCapturer::Callback {
+ public:
+ void SetUp() override {
+ com_initializer_ =
+ std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
+ EXPECT_TRUE(com_initializer_->Succeeded());
+
+ if (!IsWgcSupported(GetParam())) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+ }
+
+ void SetUpForWindowCapture(int window_width = kMediumWindowWidth,
+ int window_height = kMediumWindowHeight) {
+ capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ CreateWindowOnSeparateThread(window_width, window_height);
+ StartWindowThreadMessageLoop();
+ source_id_ = GetTestWindowIdFromSourceList();
+ }
+
+ void SetUpForScreenCapture() {
+ capturer_ = WgcCapturerWin::CreateRawScreenCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ source_id_ = GetScreenIdFromSourceList();
+ }
+
+ void TearDown() override {
+ if (window_open_) {
+ CloseTestWindow();
+ }
+ }
+
+ // The window must live on a separate thread so that we can run a message pump
+ // without blocking the test thread. This is necessary if we are interested in
+ // having GraphicsCaptureItem events (i.e. the Closed event) fire, and it more
+ // closely resembles how capture works in the wild.
+ void CreateWindowOnSeparateThread(int window_width, int window_height) {
+ window_thread_ = rtc::Thread::Create();
+ window_thread_->SetName(kWindowThreadName, nullptr);
+ window_thread_->Start();
+ SendTask(window_thread_.get(), [this, window_width, window_height]() {
+ window_thread_id_ = GetCurrentThreadId();
+ window_info_ =
+ CreateTestWindow(kWindowTitle, window_height, window_width);
+ window_open_ = true;
+
+ while (!IsWindowResponding(window_info_.hwnd)) {
+ RTC_LOG(LS_INFO) << "Waiting for test window to become responsive in "
+ "WgcWindowCaptureTest.";
+ }
+
+ while (!IsWindowValidAndVisible(window_info_.hwnd)) {
+ RTC_LOG(LS_INFO) << "Waiting for test window to be visible in "
+ "WgcWindowCaptureTest.";
+ }
+ });
+
+ ASSERT_TRUE(window_thread_->RunningForTest());
+ ASSERT_FALSE(window_thread_->IsCurrent());
+ }
+
+ void StartWindowThreadMessageLoop() {
+ window_thread_->PostTask([this]() {
+ MSG msg;
+ BOOL gm;
+ while ((gm = ::GetMessage(&msg, NULL, 0, 0)) != 0 && gm != -1) {
+ ::DispatchMessage(&msg);
+ if (msg.message == kDestroyWindow) {
+ DestroyTestWindow(window_info_);
+ }
+ if (msg.message == kQuitRunning) {
+ PostQuitMessage(0);
+ }
+ }
+ });
+ }
+
+ void CloseTestWindow() {
+ ::PostThreadMessage(window_thread_id_, kDestroyWindow, 0, 0);
+ ::PostThreadMessage(window_thread_id_, kQuitRunning, 0, 0);
+ window_thread_->Stop();
+ window_open_ = false;
+ }
+
+ DesktopCapturer::SourceId GetTestWindowIdFromSourceList() {
+ // Frequently, the test window will not show up in GetSourceList because it
+ // was created too recently. Since we are confident the window will be found
+ // eventually we loop here until we find it.
+ intptr_t src_id = 0;
+ do {
+ DesktopCapturer::SourceList sources;
+ EXPECT_TRUE(capturer_->GetSourceList(&sources));
+ auto it = std::find_if(
+ sources.begin(), sources.end(),
+ [&](const DesktopCapturer::Source& src) {
+ return src.id == reinterpret_cast<intptr_t>(window_info_.hwnd);
+ });
+
+ if (it != sources.end())
+ src_id = it->id;
+ } while (src_id != reinterpret_cast<intptr_t>(window_info_.hwnd));
+
+ return src_id;
+ }
+
+ DesktopCapturer::SourceId GetScreenIdFromSourceList() {
+ DesktopCapturer::SourceList sources;
+ EXPECT_TRUE(capturer_->GetSourceList(&sources));
+ EXPECT_GT(sources.size(), 0ULL);
+ return sources[0].id;
+ }
+
+ void DoCapture(int num_captures = 1) {
+ // Capture the requested number of frames. We expect the first capture to
+ // always succeed. If we're asked for multiple frames, we do expect to see a
+ // a couple dropped frames due to resizing the window.
+ const int max_tries = num_captures == 1 ? 1 : kMaxTries;
+ int success_count = 0;
+ for (int i = 0; success_count < num_captures && i < max_tries; i++) {
+ capturer_->CaptureFrame();
+ if (result_ == DesktopCapturer::Result::ERROR_PERMANENT)
+ break;
+ if (result_ == DesktopCapturer::Result::SUCCESS)
+ success_count++;
+ }
+
+ total_successful_captures_ += success_count;
+ EXPECT_EQ(success_count, num_captures);
+ EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS);
+ EXPECT_TRUE(frame_);
+ EXPECT_GE(metrics::NumEvents(kCapturerResultHistogram, kSuccess),
+ total_successful_captures_);
+ }
+
+ void ValidateFrame(int expected_width, int expected_height) {
+ EXPECT_EQ(frame_->size().width(), expected_width - kWindowWidthSubtrahend);
+ EXPECT_EQ(frame_->size().height(),
+ expected_height - kWindowHeightSubtrahend);
+
+ // Verify the buffer contains as much data as it should.
+ int data_length = frame_->stride() * frame_->size().height();
+
+ // The first and last pixel should have the same color because they will be
+ // from the border of the window.
+ // Pixels have 4 bytes of data so the whole pixel needs a uint32_t to fit.
+ uint32_t first_pixel = static_cast<uint32_t>(*frame_->data());
+ uint32_t last_pixel = static_cast<uint32_t>(
+ *(frame_->data() + data_length - DesktopFrame::kBytesPerPixel));
+ EXPECT_EQ(first_pixel, last_pixel);
+
+ // Let's also check a pixel from the middle of the content area, which the
+ // test window will paint a consistent color for us to verify.
+ uint8_t* middle_pixel = frame_->data() + (data_length / 2);
+
+ int sub_pixel_offset = DesktopFrame::kBytesPerPixel / 4;
+ EXPECT_EQ(*middle_pixel, kTestWindowBValue);
+ middle_pixel += sub_pixel_offset;
+ EXPECT_EQ(*middle_pixel, kTestWindowGValue);
+ middle_pixel += sub_pixel_offset;
+ EXPECT_EQ(*middle_pixel, kTestWindowRValue);
+ middle_pixel += sub_pixel_offset;
+
+ // The window is opaque so we expect 0xFF for the Alpha channel.
+ EXPECT_EQ(*middle_pixel, 0xFF);
+ }
+
+ // DesktopCapturer::Callback interface
+ // The capturer synchronously invokes this method before `CaptureFrame()`
+ // returns.
+ void OnCaptureResult(DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) override {
+ result_ = result;
+ frame_ = std::move(frame);
+ }
+
+ protected:
+ std::unique_ptr<ScopedCOMInitializer> com_initializer_;
+ DWORD window_thread_id_;
+ std::unique_ptr<rtc::Thread> window_thread_;
+ WindowInfo window_info_;
+ intptr_t source_id_;
+ bool window_open_ = false;
+ DesktopCapturer::Result result_;
+ int total_successful_captures_ = 0;
+ std::unique_ptr<DesktopFrame> frame_;
+ std::unique_ptr<DesktopCapturer> capturer_;
+};
+
+TEST_P(WgcCapturerWinTest, SelectValidSource) {
+ if (GetParam() == CaptureType::kWindow) {
+ SetUpForWindowCapture();
+ } else {
+ SetUpForScreenCapture();
+ }
+
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+}
+
+TEST_P(WgcCapturerWinTest, SelectInvalidSource) {
+ if (GetParam() == CaptureType::kWindow) {
+ capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ source_id_ = kNullWindowId;
+ } else {
+ capturer_ = WgcCapturerWin::CreateRawScreenCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ source_id_ = kInvalidScreenId;
+ }
+
+ EXPECT_FALSE(capturer_->SelectSource(source_id_));
+}
+
+TEST_P(WgcCapturerWinTest, Capture) {
+ if (GetParam() == CaptureType::kWindow) {
+ SetUpForWindowCapture();
+ } else {
+ SetUpForScreenCapture();
+ }
+
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ capturer_->Start(this);
+ EXPECT_GE(metrics::NumEvents(kCapturerImplHistogram,
+ DesktopCapturerId::kWgcCapturerWin),
+ 1);
+
+ DoCapture();
+ EXPECT_GT(frame_->size().width(), 0);
+ EXPECT_GT(frame_->size().height(), 0);
+}
+
+TEST_P(WgcCapturerWinTest, CaptureTime) {
+ if (GetParam() == CaptureType::kWindow) {
+ SetUpForWindowCapture();
+ } else {
+ SetUpForScreenCapture();
+ }
+
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+ capturer_->Start(this);
+
+ int64_t start_time;
+ start_time = rtc::TimeNanos();
+ capturer_->CaptureFrame();
+
+ int capture_time_ms =
+ (rtc::TimeNanos() - start_time) / rtc::kNumNanosecsPerMillisec;
+ EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS);
+ EXPECT_TRUE(frame_);
+
+ // The test may measure the time slightly differently than the capturer. So we
+ // just check if it's within 5 ms.
+ EXPECT_NEAR(frame_->capture_time_ms(), capture_time_ms, 5);
+ EXPECT_GE(
+ metrics::NumEvents(kCaptureTimeHistogram, frame_->capture_time_ms()), 1);
+}
+
+INSTANTIATE_TEST_SUITE_P(SourceAgnostic,
+ WgcCapturerWinTest,
+ ::testing::Values(CaptureType::kWindow,
+ CaptureType::kScreen));
+
+TEST(WgcCapturerNoMonitorTest, NoMonitors) {
+ ScopedCOMInitializer com_initializer(ScopedCOMInitializer::kMTA);
+ EXPECT_TRUE(com_initializer.Succeeded());
+ if (HasActiveDisplay()) {
+ RTC_LOG(LS_INFO) << "Skip WgcCapturerWinTest designed specifically for "
+ "systems with no monitors";
+ GTEST_SKIP();
+ }
+
+ // A bug in `CreateForMonitor` prevents screen capture when no displays are
+ // attached.
+ EXPECT_FALSE(IsWgcSupported(CaptureType::kScreen));
+
+ // A bug in the DWM (Desktop Window Manager) prevents it from providing image
+ // data if there are no displays attached. This was fixed in Windows 11.
+ if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN11)
+ EXPECT_FALSE(IsWgcSupported(CaptureType::kWindow));
+ else
+ EXPECT_TRUE(IsWgcSupported(CaptureType::kWindow));
+}
+
+class WgcCapturerMonitorTest : public WgcCapturerWinTest {
+ public:
+ void SetUp() {
+ com_initializer_ =
+ std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
+ EXPECT_TRUE(com_initializer_->Succeeded());
+
+ if (!IsWgcSupported(CaptureType::kScreen)) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+ }
+};
+
+TEST_F(WgcCapturerMonitorTest, FocusOnMonitor) {
+ SetUpForScreenCapture();
+ EXPECT_TRUE(capturer_->SelectSource(0));
+
+ // You can't set focus on a monitor.
+ EXPECT_FALSE(capturer_->FocusOnSelectedSource());
+}
+
+TEST_F(WgcCapturerMonitorTest, CaptureAllMonitors) {
+ SetUpForScreenCapture();
+ EXPECT_TRUE(capturer_->SelectSource(kFullDesktopScreenId));
+
+ capturer_->Start(this);
+ DoCapture();
+ EXPECT_GT(frame_->size().width(), 0);
+ EXPECT_GT(frame_->size().height(), 0);
+}
+
+class WgcCapturerWindowTest : public WgcCapturerWinTest {
+ public:
+ void SetUp() {
+ com_initializer_ =
+ std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
+ EXPECT_TRUE(com_initializer_->Succeeded());
+
+ if (!IsWgcSupported(CaptureType::kWindow)) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+ }
+};
+
+TEST_F(WgcCapturerWindowTest, FocusOnWindow) {
+ capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ window_info_ = CreateTestWindow(kWindowTitle);
+ source_id_ = GetScreenIdFromSourceList();
+
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+ EXPECT_TRUE(capturer_->FocusOnSelectedSource());
+
+ HWND hwnd = reinterpret_cast<HWND>(source_id_);
+ EXPECT_EQ(hwnd, ::GetActiveWindow());
+ EXPECT_EQ(hwnd, ::GetForegroundWindow());
+ EXPECT_EQ(hwnd, ::GetFocus());
+ DestroyTestWindow(window_info_);
+}
+
+TEST_F(WgcCapturerWindowTest, SelectMinimizedWindow) {
+ SetUpForWindowCapture();
+ MinimizeTestWindow(reinterpret_cast<HWND>(source_id_));
+ EXPECT_FALSE(capturer_->SelectSource(source_id_));
+
+ UnminimizeTestWindow(reinterpret_cast<HWND>(source_id_));
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+}
+
+TEST_F(WgcCapturerWindowTest, SelectClosedWindow) {
+ SetUpForWindowCapture();
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ CloseTestWindow();
+ EXPECT_FALSE(capturer_->SelectSource(source_id_));
+}
+
+TEST_F(WgcCapturerWindowTest, UnsupportedWindowStyle) {
+ // Create a window with the WS_EX_TOOLWINDOW style, which WGC does not
+ // support.
+ window_info_ = CreateTestWindow(kWindowTitle, kMediumWindowWidth,
+ kMediumWindowHeight, WS_EX_TOOLWINDOW);
+ capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ DesktopCapturer::SourceList sources;
+ EXPECT_TRUE(capturer_->GetSourceList(&sources));
+ auto it = std::find_if(
+ sources.begin(), sources.end(), [&](const DesktopCapturer::Source& src) {
+ return src.id == reinterpret_cast<intptr_t>(window_info_.hwnd);
+ });
+
+ // We should not find the window, since we filter for unsupported styles.
+ EXPECT_EQ(it, sources.end());
+ DestroyTestWindow(window_info_);
+}
+
+TEST_F(WgcCapturerWindowTest, IncreaseWindowSizeMidCapture) {
+ SetUpForWindowCapture(kSmallWindowWidth, kSmallWindowHeight);
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ capturer_->Start(this);
+ DoCapture();
+ ValidateFrame(kSmallWindowWidth, kSmallWindowHeight);
+
+ ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight);
+ DoCapture(kNumCapturesToFlushBuffers);
+ ValidateFrame(kSmallWindowWidth, kMediumWindowHeight);
+
+ ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight);
+ DoCapture(kNumCapturesToFlushBuffers);
+ ValidateFrame(kLargeWindowWidth, kMediumWindowHeight);
+}
+
+TEST_F(WgcCapturerWindowTest, ReduceWindowSizeMidCapture) {
+ SetUpForWindowCapture(kLargeWindowWidth, kLargeWindowHeight);
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ capturer_->Start(this);
+ DoCapture();
+ ValidateFrame(kLargeWindowWidth, kLargeWindowHeight);
+
+ ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight);
+ DoCapture(kNumCapturesToFlushBuffers);
+ ValidateFrame(kLargeWindowWidth, kMediumWindowHeight);
+
+ ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight);
+ DoCapture(kNumCapturesToFlushBuffers);
+ ValidateFrame(kSmallWindowWidth, kMediumWindowHeight);
+}
+
+TEST_F(WgcCapturerWindowTest, MinimizeWindowMidCapture) {
+ SetUpForWindowCapture();
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ capturer_->Start(this);
+
+ // Minmize the window and capture should continue but return temporary errors.
+ MinimizeTestWindow(window_info_.hwnd);
+ for (int i = 0; i < 5; ++i) {
+ capturer_->CaptureFrame();
+ EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_TEMPORARY);
+ }
+
+ // Reopen the window and the capture should continue normally.
+ UnminimizeTestWindow(window_info_.hwnd);
+ DoCapture();
+ // We can't verify the window size here because the test window does not
+ // repaint itself after it is unminimized, but capturing successfully is still
+ // a good test.
+}
+
+TEST_F(WgcCapturerWindowTest, CloseWindowMidCapture) {
+ SetUpForWindowCapture();
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ capturer_->Start(this);
+ DoCapture();
+ ValidateFrame(kMediumWindowWidth, kMediumWindowHeight);
+
+ CloseTestWindow();
+
+ // We need to pump our message queue so the Closed event will be delivered to
+ // the capturer's event handler. If we are too early and the Closed event
+ // hasn't arrived yet we should keep trying until the capturer receives it and
+ // stops.
+ auto* wgc_capturer = static_cast<WgcCapturerWin*>(capturer_.get());
+ MSG msg;
+ for (int i = 0;
+ wgc_capturer->IsSourceBeingCaptured(source_id_) && i < kMaxTries; ++i) {
+ // Unlike GetMessage, PeekMessage will not hang if there are no messages in
+ // the queue.
+ PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
+ SleepMs(1);
+ }
+
+ EXPECT_FALSE(wgc_capturer->IsSourceBeingCaptured(source_id_));
+
+ // The frame pool can buffer `kNumBuffers` frames. We must consume these
+ // and then make one more call to CaptureFrame before we expect to see the
+ // failure.
+ int num_tries = 0;
+ do {
+ capturer_->CaptureFrame();
+ } while (result_ == DesktopCapturer::Result::SUCCESS &&
+ ++num_tries <= WgcCaptureSession::kNumBuffers);
+
+ EXPECT_GE(metrics::NumEvents(kCapturerResultHistogram, kSessionStartFailure),
+ 1);
+ EXPECT_GE(metrics::NumEvents(kCaptureSessionResultHistogram, kSourceClosed),
+ 1);
+ EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_PERMANENT);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.cc
new file mode 100644
index 0000000000..dd9009120b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.cc
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2020 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 "modules/desktop_capture/win/wgc_desktop_frame.h"
+
+#include <utility>
+
+namespace webrtc {
+
+WgcDesktopFrame::WgcDesktopFrame(DesktopSize size,
+ int stride,
+ std::vector<uint8_t>&& image_data)
+ : DesktopFrame(size, stride, image_data.data(), nullptr),
+ image_data_(std::move(image_data)) {}
+
+WgcDesktopFrame::~WgcDesktopFrame() = default;
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.h b/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.h
new file mode 100644
index 0000000000..0a671cf2f8
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 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 MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_
+
+#include <d3d11.h>
+#include <wrl/client.h>
+
+#include <memory>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+// DesktopFrame implementation used by capturers that use the
+// Windows.Graphics.Capture API.
+class WgcDesktopFrame final : public DesktopFrame {
+ public:
+ // WgcDesktopFrame receives an rvalue reference to the `image_data` vector
+ // so that it can take ownership of it (and avoid a copy).
+ WgcDesktopFrame(DesktopSize size,
+ int stride,
+ std::vector<uint8_t>&& image_data);
+
+ WgcDesktopFrame(const WgcDesktopFrame&) = delete;
+ WgcDesktopFrame& operator=(const WgcDesktopFrame&) = delete;
+
+ ~WgcDesktopFrame() override;
+
+ private:
+ std::vector<uint8_t> image_data_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.cc b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.cc
new file mode 100644
index 0000000000..d58c02e17c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.cc
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2014 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 "modules/desktop_capture/win/window_capture_utils.h"
+
+// Just for the DWMWINDOWATTRIBUTE enums (DWMWA_CLOAKED).
+#include <dwmapi.h>
+
+#include <algorithm>
+
+#include "modules/desktop_capture/win/scoped_gdi_object.h"
+#include "rtc_base/arraysize.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/string_utils.h"
+#include "rtc_base/win/windows_version.h"
+
+namespace webrtc {
+
+namespace {
+
+struct GetWindowListParams {
+ GetWindowListParams(int flags,
+ LONG ex_style_filters,
+ DesktopCapturer::SourceList* result)
+ : ignore_untitled(flags & GetWindowListFlags::kIgnoreUntitled),
+ ignore_unresponsive(flags & GetWindowListFlags::kIgnoreUnresponsive),
+ ignore_current_process_windows(
+ flags & GetWindowListFlags::kIgnoreCurrentProcessWindows),
+ ex_style_filters(ex_style_filters),
+ result(result) {}
+ const bool ignore_untitled;
+ const bool ignore_unresponsive;
+ const bool ignore_current_process_windows;
+ const LONG ex_style_filters;
+ DesktopCapturer::SourceList* const result;
+};
+
+bool IsWindowOwnedByCurrentProcess(HWND hwnd) {
+ DWORD process_id;
+ GetWindowThreadProcessId(hwnd, &process_id);
+ return process_id == GetCurrentProcessId();
+}
+
+BOOL CALLBACK GetWindowListHandler(HWND hwnd, LPARAM param) {
+ GetWindowListParams* params = reinterpret_cast<GetWindowListParams*>(param);
+ DesktopCapturer::SourceList* list = params->result;
+
+ // Skip invisible and minimized windows
+ if (!IsWindowVisible(hwnd) || IsIconic(hwnd)) {
+ return TRUE;
+ }
+
+ // Skip windows which are not presented in the taskbar,
+ // namely owned window if they don't have the app window style set
+ HWND owner = GetWindow(hwnd, GW_OWNER);
+ LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+ if (owner && !(exstyle & WS_EX_APPWINDOW)) {
+ return TRUE;
+ }
+
+ // Filter out windows that match the extended styles the caller has specified,
+ // e.g. WS_EX_TOOLWINDOW for capturers that don't support overlay windows.
+ if (exstyle & params->ex_style_filters) {
+ return TRUE;
+ }
+
+ if (params->ignore_unresponsive && !IsWindowResponding(hwnd)) {
+ return TRUE;
+ }
+
+ DesktopCapturer::Source window;
+ window.id = reinterpret_cast<WindowId>(hwnd);
+
+ DWORD pid;
+ GetWindowThreadProcessId(hwnd, &pid);
+ window.pid = static_cast<pid_t>(pid);
+
+ // GetWindowText* are potentially blocking operations if `hwnd` is
+ // owned by the current process. The APIs will send messages to the window's
+ // message loop, and if the message loop is waiting on this operation we will
+ // enter a deadlock.
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowtexta#remarks
+ //
+ // To help consumers avoid this, there is a DesktopCaptureOption to ignore
+ // windows owned by the current process. Consumers should either ensure that
+ // the thread running their message loop never waits on this operation, or use
+ // the option to exclude these windows from the source list.
+ bool owned_by_current_process = IsWindowOwnedByCurrentProcess(hwnd);
+ if (owned_by_current_process && params->ignore_current_process_windows) {
+ return TRUE;
+ }
+
+ // Even if consumers request to enumerate windows owned by the current
+ // process, we should not call GetWindowText* on unresponsive windows owned by
+ // the current process because we will hang. Unfortunately, we could still
+ // hang if the window becomes unresponsive after this check, hence the option
+ // to avoid these completely.
+ if (!owned_by_current_process || IsWindowResponding(hwnd)) {
+ const size_t kTitleLength = 500;
+ WCHAR window_title[kTitleLength] = L"";
+ if (GetWindowTextLength(hwnd) != 0 &&
+ GetWindowTextW(hwnd, window_title, kTitleLength) > 0) {
+ window.title = rtc::ToUtf8(window_title);
+ }
+ }
+
+ // Skip windows when we failed to convert the title or it is empty.
+ if (params->ignore_untitled && window.title.empty())
+ return TRUE;
+
+ // Capture the window class name, to allow specific window classes to be
+ // skipped.
+ //
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassa
+ // says lpszClassName field in WNDCLASS is limited by 256 symbols, so we don't
+ // need to have a buffer bigger than that.
+ const size_t kMaxClassNameLength = 256;
+ WCHAR class_name[kMaxClassNameLength] = L"";
+ const int class_name_length =
+ GetClassNameW(hwnd, class_name, kMaxClassNameLength);
+ if (class_name_length < 1)
+ return TRUE;
+
+ // Skip Program Manager window.
+ if (wcscmp(class_name, L"Progman") == 0)
+ return TRUE;
+
+ // Skip Start button window on Windows Vista, Windows 7.
+ // On Windows 8, Windows 8.1, Windows 10 Start button is not a top level
+ // window, so it will not be examined here.
+ if (wcscmp(class_name, L"Button") == 0)
+ return TRUE;
+
+ list->push_back(window);
+
+ return TRUE;
+}
+
+} // namespace
+
+// Prefix used to match the window class for Chrome windows.
+const wchar_t kChromeWindowClassPrefix[] = L"Chrome_WidgetWin_";
+
+// The hiddgen taskbar will leave a 2 pixel margin on the screen.
+const int kHiddenTaskbarMarginOnScreen = 2;
+
+bool GetWindowRect(HWND window, DesktopRect* result) {
+ RECT rect;
+ if (!::GetWindowRect(window, &rect)) {
+ return false;
+ }
+ *result = DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+ return true;
+}
+
+bool GetCroppedWindowRect(HWND window,
+ bool avoid_cropping_border,
+ DesktopRect* cropped_rect,
+ DesktopRect* original_rect) {
+ DesktopRect window_rect;
+ if (!GetWindowRect(window, &window_rect)) {
+ return false;
+ }
+
+ if (original_rect) {
+ *original_rect = window_rect;
+ }
+ *cropped_rect = window_rect;
+
+ bool is_maximized = false;
+ if (!IsWindowMaximized(window, &is_maximized)) {
+ return false;
+ }
+
+ // As of Windows8, transparent resize borders are added by the OS at
+ // left/bottom/right sides of a resizeable window. If the cropped window
+ // doesn't remove these borders, the background will be exposed a bit.
+ if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN8 ||
+ is_maximized) {
+ // Only apply this cropping to windows with a resize border (otherwise,
+ // it'd clip the edges of captured pop-up windows without this border).
+ LONG style = GetWindowLong(window, GWL_STYLE);
+ if (style & WS_THICKFRAME || style & DS_MODALFRAME) {
+ int width = GetSystemMetrics(SM_CXSIZEFRAME);
+ int bottom_height = GetSystemMetrics(SM_CYSIZEFRAME);
+ const int visible_border_height = GetSystemMetrics(SM_CYBORDER);
+ int top_height = visible_border_height;
+
+ // If requested, avoid cropping the visible window border. This is used
+ // for pop-up windows to include their border, but not for the outermost
+ // window (where a partially-transparent border may expose the
+ // background a bit).
+ if (avoid_cropping_border) {
+ width = std::max(0, width - GetSystemMetrics(SM_CXBORDER));
+ bottom_height = std::max(0, bottom_height - visible_border_height);
+ top_height = 0;
+ }
+ cropped_rect->Extend(-width, -top_height, -width, -bottom_height);
+ }
+ }
+
+ return true;
+}
+
+bool GetWindowContentRect(HWND window, DesktopRect* result) {
+ if (!GetWindowRect(window, result)) {
+ return false;
+ }
+
+ RECT rect;
+ if (!::GetClientRect(window, &rect)) {
+ return false;
+ }
+
+ const int width = rect.right - rect.left;
+ // The GetClientRect() is not expected to return a larger area than
+ // GetWindowRect().
+ if (width > 0 && width < result->width()) {
+ // - GetClientRect() always set the left / top of RECT to 0. So we need to
+ // estimate the border width from GetClientRect() and GetWindowRect().
+ // - Border width of a window varies according to the window type.
+ // - GetClientRect() excludes the title bar, which should be considered as
+ // part of the content and included in the captured frame. So we always
+ // estimate the border width according to the window width.
+ // - We assume a window has same border width in each side.
+ // So we shrink half of the width difference from all four sides.
+ const int shrink = ((width - result->width()) / 2);
+ // When `shrink` is negative, DesktopRect::Extend() shrinks itself.
+ result->Extend(shrink, 0, shrink, 0);
+ // Usually this should not happen, just in case we have received a strange
+ // window, which has only left and right borders.
+ if (result->height() > shrink * 2) {
+ result->Extend(0, shrink, 0, shrink);
+ }
+ RTC_DCHECK(!result->is_empty());
+ }
+
+ return true;
+}
+
+int GetWindowRegionTypeWithBoundary(HWND window, DesktopRect* result) {
+ win::ScopedGDIObject<HRGN, win::DeleteObjectTraits<HRGN>> scoped_hrgn(
+ CreateRectRgn(0, 0, 0, 0));
+ const int region_type = GetWindowRgn(window, scoped_hrgn.Get());
+
+ if (region_type == SIMPLEREGION) {
+ RECT rect;
+ GetRgnBox(scoped_hrgn.Get(), &rect);
+ *result =
+ DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+ }
+ return region_type;
+}
+
+bool GetDcSize(HDC hdc, DesktopSize* size) {
+ win::ScopedGDIObject<HGDIOBJ, win::DeleteObjectTraits<HGDIOBJ>> scoped_hgdi(
+ GetCurrentObject(hdc, OBJ_BITMAP));
+ BITMAP bitmap;
+ memset(&bitmap, 0, sizeof(BITMAP));
+ if (GetObject(scoped_hgdi.Get(), sizeof(BITMAP), &bitmap) == 0) {
+ return false;
+ }
+ size->set(bitmap.bmWidth, bitmap.bmHeight);
+ return true;
+}
+
+bool IsWindowMaximized(HWND window, bool* result) {
+ WINDOWPLACEMENT placement;
+ memset(&placement, 0, sizeof(WINDOWPLACEMENT));
+ placement.length = sizeof(WINDOWPLACEMENT);
+ if (!::GetWindowPlacement(window, &placement)) {
+ return false;
+ }
+
+ *result = (placement.showCmd == SW_SHOWMAXIMIZED);
+ return true;
+}
+
+bool IsWindowValidAndVisible(HWND window) {
+ return IsWindow(window) && IsWindowVisible(window) && !IsIconic(window);
+}
+
+bool IsWindowResponding(HWND window) {
+ // 50ms is chosen in case the system is under heavy load, but it's also not
+ // too long to delay window enumeration considerably.
+ const UINT uTimeoutMs = 50;
+ return SendMessageTimeout(window, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, uTimeoutMs,
+ nullptr);
+}
+
+bool GetWindowList(int flags,
+ DesktopCapturer::SourceList* windows,
+ LONG ex_style_filters) {
+ GetWindowListParams params(flags, ex_style_filters, windows);
+ return ::EnumWindows(&GetWindowListHandler,
+ reinterpret_cast<LPARAM>(&params)) != 0;
+}
+
+// WindowCaptureHelperWin implementation.
+WindowCaptureHelperWin::WindowCaptureHelperWin() {
+ // Try to load dwmapi.dll dynamically since it is not available on XP.
+ dwmapi_library_ = LoadLibraryW(L"dwmapi.dll");
+ if (dwmapi_library_) {
+ func_ = reinterpret_cast<DwmIsCompositionEnabledFunc>(
+ GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled"));
+ dwm_get_window_attribute_func_ =
+ reinterpret_cast<DwmGetWindowAttributeFunc>(
+ GetProcAddress(dwmapi_library_, "DwmGetWindowAttribute"));
+ }
+
+ if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN10) {
+ if (FAILED(::CoCreateInstance(__uuidof(VirtualDesktopManager), nullptr,
+ CLSCTX_ALL,
+ IID_PPV_ARGS(&virtual_desktop_manager_)))) {
+ RTC_LOG(LS_WARNING) << "Fail to create instance of VirtualDesktopManager";
+ }
+ }
+}
+
+WindowCaptureHelperWin::~WindowCaptureHelperWin() {
+ if (dwmapi_library_) {
+ FreeLibrary(dwmapi_library_);
+ }
+}
+
+bool WindowCaptureHelperWin::IsAeroEnabled() {
+ BOOL result = FALSE;
+ if (func_) {
+ func_(&result);
+ }
+ return result != FALSE;
+}
+
+// This is just a best guess of a notification window. Chrome uses the Windows
+// native framework for showing notifications. So far what we know about such a
+// window includes: no title, class name with prefix "Chrome_WidgetWin_" and
+// with certain extended styles.
+bool WindowCaptureHelperWin::IsWindowChromeNotification(HWND hwnd) {
+ const size_t kTitleLength = 32;
+ WCHAR window_title[kTitleLength];
+ GetWindowTextW(hwnd, window_title, kTitleLength);
+ if (wcsnlen_s(window_title, kTitleLength) != 0) {
+ return false;
+ }
+
+ const size_t kClassLength = 256;
+ WCHAR class_name[kClassLength];
+ const int class_name_length = GetClassNameW(hwnd, class_name, kClassLength);
+ if (class_name_length < 1 ||
+ wcsncmp(class_name, kChromeWindowClassPrefix,
+ wcsnlen_s(kChromeWindowClassPrefix, kClassLength)) != 0) {
+ return false;
+ }
+
+ const LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+ if ((exstyle & WS_EX_NOACTIVATE) && (exstyle & WS_EX_TOOLWINDOW) &&
+ (exstyle & WS_EX_TOPMOST)) {
+ return true;
+ }
+
+ return false;
+}
+
+// `content_rect` is preferred because,
+// 1. WindowCapturerWinGdi is using GDI capturer, which cannot capture DX
+// output.
+// So ScreenCapturer should be used as much as possible to avoid
+// uncapturable cases. Note: lots of new applications are using DX output
+// (hardware acceleration) to improve the performance which cannot be
+// captured by WindowCapturerWinGdi. See bug http://crbug.com/741770.
+// 2. WindowCapturerWinGdi is still useful because we do not want to expose the
+// content on other windows if the target window is covered by them.
+// 3. Shadow and borders should not be considered as "content" on other
+// windows because they do not expose any useful information.
+//
+// So we can bear the false-negative cases (target window is covered by the
+// borders or shadow of other windows, but we have not detected it) in favor
+// of using ScreenCapturer, rather than let the false-positive cases (target
+// windows is only covered by borders or shadow of other windows, but we treat
+// it as overlapping) impact the user experience.
+bool WindowCaptureHelperWin::AreWindowsOverlapping(
+ HWND hwnd,
+ HWND selected_hwnd,
+ const DesktopRect& selected_window_rect) {
+ DesktopRect content_rect;
+ if (!GetWindowContentRect(hwnd, &content_rect)) {
+ // Bail out if failed to get the window area.
+ return true;
+ }
+ content_rect.IntersectWith(selected_window_rect);
+
+ if (content_rect.is_empty()) {
+ return false;
+ }
+
+ // When the taskbar is automatically hidden, it will leave a 2 pixel margin on
+ // the screen which will overlap the maximized selected window that will use
+ // up the full screen area. Since there is no solid way to identify a hidden
+ // taskbar window, we have to make an exemption here if the overlapping is
+ // 2 x screen_width/height to a maximized window.
+ bool is_maximized = false;
+ IsWindowMaximized(selected_hwnd, &is_maximized);
+ bool overlaps_hidden_horizontal_taskbar =
+ selected_window_rect.width() == content_rect.width() &&
+ content_rect.height() == kHiddenTaskbarMarginOnScreen;
+ bool overlaps_hidden_vertical_taskbar =
+ selected_window_rect.height() == content_rect.height() &&
+ content_rect.width() == kHiddenTaskbarMarginOnScreen;
+ if (is_maximized && (overlaps_hidden_horizontal_taskbar ||
+ overlaps_hidden_vertical_taskbar)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool WindowCaptureHelperWin::IsWindowOnCurrentDesktop(HWND hwnd) {
+ // Make sure the window is on the current virtual desktop.
+ if (virtual_desktop_manager_) {
+ BOOL on_current_desktop;
+ if (SUCCEEDED(virtual_desktop_manager_->IsWindowOnCurrentVirtualDesktop(
+ hwnd, &on_current_desktop)) &&
+ !on_current_desktop) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool WindowCaptureHelperWin::IsWindowVisibleOnCurrentDesktop(HWND hwnd) {
+ return IsWindowValidAndVisible(hwnd) && IsWindowOnCurrentDesktop(hwnd) &&
+ !IsWindowCloaked(hwnd);
+}
+
+// A cloaked window is composited but not visible to the user.
+// Example: Cortana or the Action Center when collapsed.
+bool WindowCaptureHelperWin::IsWindowCloaked(HWND hwnd) {
+ if (!dwm_get_window_attribute_func_) {
+ // Does not apply.
+ return false;
+ }
+
+ int res = 0;
+ if (dwm_get_window_attribute_func_(hwnd, DWMWA_CLOAKED, &res, sizeof(res)) !=
+ S_OK) {
+ // Cannot tell so assume not cloaked for backward compatibility.
+ return false;
+ }
+
+ return res != 0;
+}
+
+bool WindowCaptureHelperWin::EnumerateCapturableWindows(
+ DesktopCapturer::SourceList* results,
+ bool enumerate_current_process_windows,
+ LONG ex_style_filters) {
+ int flags = (GetWindowListFlags::kIgnoreUntitled |
+ GetWindowListFlags::kIgnoreUnresponsive);
+ if (!enumerate_current_process_windows) {
+ flags |= GetWindowListFlags::kIgnoreCurrentProcessWindows;
+ }
+
+ if (!webrtc::GetWindowList(flags, results, ex_style_filters)) {
+ return false;
+ }
+
+ for (auto it = results->begin(); it != results->end();) {
+ if (!IsWindowVisibleOnCurrentDesktop(reinterpret_cast<HWND>(it->id))) {
+ it = results->erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.h b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.h
new file mode 100644
index 0000000000..caea07958d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2014 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 MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURE_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURE_UTILS_H_
+
+#include <shlobj.h>
+#include <windows.h>
+#include <wrl/client.h>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+// Outputs the window rect. The returned DesktopRect is in system coordinates,
+// i.e. the primary monitor on the system always starts from (0, 0). This
+// function returns false if native APIs fail.
+bool GetWindowRect(HWND window, DesktopRect* result);
+
+// Outputs the window rect, with the left/right/bottom frame border cropped if
+// the window is maximized or has a transparent resize border.
+// `avoid_cropping_border` may be set to true to avoid cropping the visible
+// border when cropping any resize border.
+// `cropped_rect` is the cropped rect relative to the
+// desktop. `original_rect` is the original rect returned from GetWindowRect.
+// Returns true if all API calls succeeded. The returned DesktopRect is in
+// system coordinates, i.e. the primary monitor on the system always starts from
+// (0, 0). `original_rect` can be nullptr.
+//
+// TODO(zijiehe): Move this function to CroppingWindowCapturerWin after it has
+// been removed from MouseCursorMonitorWin.
+// This function should only be used by CroppingWindowCapturerWin. Instead a
+// DesktopRect CropWindowRect(const DesktopRect& rect)
+// should be added as a utility function to help CroppingWindowCapturerWin and
+// WindowCapturerWinGdi to crop out the borders or shadow according to their
+// scenarios. But this function is too generic and easy to be misused.
+bool GetCroppedWindowRect(HWND window,
+ bool avoid_cropping_border,
+ DesktopRect* cropped_rect,
+ DesktopRect* original_rect);
+
+// Retrieves the rectangle of the content area of `window`. Usually it contains
+// title bar and window client area, but borders or shadow are excluded. The
+// returned DesktopRect is in system coordinates, i.e. the primary monitor on
+// the system always starts from (0, 0). This function returns false if native
+// APIs fail.
+bool GetWindowContentRect(HWND window, DesktopRect* result);
+
+// Returns the region type of the `window` and fill `rect` with the region of
+// `window` if region type is SIMPLEREGION.
+int GetWindowRegionTypeWithBoundary(HWND window, DesktopRect* result);
+
+// Retrieves the size of the `hdc`. This function returns false if native APIs
+// fail.
+bool GetDcSize(HDC hdc, DesktopSize* size);
+
+// Retrieves whether the `window` is maximized and stores in `result`. This
+// function returns false if native APIs fail.
+bool IsWindowMaximized(HWND window, bool* result);
+
+// Checks that the HWND is for a valid window, that window's visibility state is
+// visible, and that it is not minimized.
+bool IsWindowValidAndVisible(HWND window);
+
+// Checks if a window responds to a message within 50ms.
+bool IsWindowResponding(HWND window);
+
+enum GetWindowListFlags {
+ kNone = 0x00,
+ kIgnoreUntitled = 1 << 0,
+ kIgnoreUnresponsive = 1 << 1,
+ kIgnoreCurrentProcessWindows = 1 << 2,
+};
+
+// Retrieves the list of top-level windows on the screen.
+// Some windows will be ignored:
+// - Those that are invisible or minimized.
+// - Program Manager & Start menu.
+// - [with kIgnoreUntitled] windows with no title.
+// - [with kIgnoreUnresponsive] windows that are unresponsive.
+// - [with kIgnoreCurrentProcessWindows] windows owned by the current process.
+// - Any windows with extended styles that match `ex_style_filters`.
+// Returns false if native APIs failed.
+bool GetWindowList(int flags,
+ DesktopCapturer::SourceList* windows,
+ LONG ex_style_filters = 0);
+
+typedef HRESULT(WINAPI* DwmIsCompositionEnabledFunc)(BOOL* enabled);
+typedef HRESULT(WINAPI* DwmGetWindowAttributeFunc)(HWND hwnd,
+ DWORD flag,
+ PVOID result_ptr,
+ DWORD result_size);
+class WindowCaptureHelperWin {
+ public:
+ WindowCaptureHelperWin();
+ ~WindowCaptureHelperWin();
+
+ WindowCaptureHelperWin(const WindowCaptureHelperWin&) = delete;
+ WindowCaptureHelperWin& operator=(const WindowCaptureHelperWin&) = delete;
+
+ bool IsAeroEnabled();
+ bool IsWindowChromeNotification(HWND hwnd);
+ bool AreWindowsOverlapping(HWND hwnd,
+ HWND selected_hwnd,
+ const DesktopRect& selected_window_rect);
+ bool IsWindowOnCurrentDesktop(HWND hwnd);
+ bool IsWindowVisibleOnCurrentDesktop(HWND hwnd);
+ bool IsWindowCloaked(HWND hwnd);
+
+ // The optional `ex_style_filters` parameter allows callers to provide
+ // extended window styles (e.g. WS_EX_TOOLWINDOW) and prevent windows that
+ // match from being included in `results`.
+ bool EnumerateCapturableWindows(DesktopCapturer::SourceList* results,
+ bool enumerate_current_process_windows,
+ LONG ex_style_filters = 0);
+
+ private:
+ HMODULE dwmapi_library_ = nullptr;
+ DwmIsCompositionEnabledFunc func_ = nullptr;
+ DwmGetWindowAttributeFunc dwm_get_window_attribute_func_ = nullptr;
+
+ // Only used on Win10+.
+ Microsoft::WRL::ComPtr<IVirtualDesktopManager> virtual_desktop_manager_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURE_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils_unittest.cc
new file mode 100644
index 0000000000..137440b09e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils_unittest.cc
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2020 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 "modules/desktop_capture/win/window_capture_utils.h"
+
+#include <winuser.h>
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/win/test_support/test_window.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "rtc_base/thread.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+const char kWindowThreadName[] = "window_capture_utils_test_thread";
+const WCHAR kWindowTitle[] = L"Window Capture Utils Test";
+
+std::unique_ptr<rtc::Thread> SetUpUnresponsiveWindow(std::mutex& mtx,
+ WindowInfo& info) {
+ std::unique_ptr<rtc::Thread> window_thread;
+ window_thread = rtc::Thread::Create();
+ window_thread->SetName(kWindowThreadName, nullptr);
+ window_thread->Start();
+
+ SendTask(window_thread.get(), [&] { info = CreateTestWindow(kWindowTitle); });
+
+ // Intentionally create a deadlock to cause the window to become unresponsive.
+ mtx.lock();
+ window_thread->PostTask([&mtx]() {
+ mtx.lock();
+ mtx.unlock();
+ });
+
+ return window_thread;
+}
+
+} // namespace
+
+TEST(WindowCaptureUtilsTest, GetWindowList) {
+ WindowInfo info = CreateTestWindow(kWindowTitle);
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
+ EXPECT_GT(window_list.size(), 0ULL);
+ EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+ DestroyTestWindow(info);
+}
+
+TEST(WindowCaptureUtilsTest, IncludeUnresponsiveWindows) {
+ std::mutex mtx;
+ WindowInfo info;
+ std::unique_ptr<rtc::Thread> window_thread =
+ SetUpUnresponsiveWindow(mtx, info);
+
+ EXPECT_FALSE(IsWindowResponding(info.hwnd));
+
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
+ EXPECT_GT(window_list.size(), 0ULL);
+ EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+
+ mtx.unlock();
+ SendTask(window_thread.get(), [&info]() { DestroyTestWindow(info); });
+ window_thread->Stop();
+}
+
+TEST(WindowCaptureUtilsTest, IgnoreUnresponsiveWindows) {
+ std::mutex mtx;
+ WindowInfo info;
+ std::unique_ptr<rtc::Thread> window_thread =
+ SetUpUnresponsiveWindow(mtx, info);
+
+ EXPECT_FALSE(IsWindowResponding(info.hwnd));
+
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(
+ GetWindowList(GetWindowListFlags::kIgnoreUnresponsive, &window_list));
+ EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+
+ mtx.unlock();
+ SendTask(window_thread.get(), [&info]() { DestroyTestWindow(info); });
+ window_thread->Stop();
+}
+
+TEST(WindowCaptureUtilsTest, IncludeUntitledWindows) {
+ WindowInfo info = CreateTestWindow(L"");
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
+ EXPECT_GT(window_list.size(), 0ULL);
+ EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+ DestroyTestWindow(info);
+}
+
+TEST(WindowCaptureUtilsTest, IgnoreUntitledWindows) {
+ WindowInfo info = CreateTestWindow(L"");
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(GetWindowList(GetWindowListFlags::kIgnoreUntitled, &window_list));
+ EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+ DestroyTestWindow(info);
+}
+
+TEST(WindowCaptureUtilsTest, IgnoreCurrentProcessWindows) {
+ WindowInfo info = CreateTestWindow(kWindowTitle);
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(GetWindowList(GetWindowListFlags::kIgnoreCurrentProcessWindows,
+ &window_list));
+ EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+ DestroyTestWindow(info);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.cc b/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.cc
new file mode 100644
index 0000000000..bc3a762264
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.cc
@@ -0,0 +1,403 @@
+/*
+ * Copyright (c) 2020 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 "modules/desktop_capture/win/window_capturer_win_gdi.h"
+
+#include <cmath>
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "modules/desktop_capture/cropped_desktop_frame.h"
+#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame_win.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/selected_window_context.h"
+#include "rtc_base/arraysize.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/string_utils.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/trace_event.h"
+#include "rtc_base/win/windows_version.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+// Used to pass input/output data during the EnumWindows call to collect
+// owned/pop-up windows that should be captured.
+struct OwnedWindowCollectorContext : public SelectedWindowContext {
+ OwnedWindowCollectorContext(HWND selected_window,
+ DesktopRect selected_window_rect,
+ WindowCaptureHelperWin* window_capture_helper,
+ std::vector<HWND>* owned_windows)
+ : SelectedWindowContext(selected_window,
+ selected_window_rect,
+ window_capture_helper),
+ owned_windows(owned_windows) {}
+
+ std::vector<HWND>* owned_windows;
+};
+
+// Called via EnumWindows for each root window; adds owned/pop-up windows that
+// should be captured to a vector it's passed.
+BOOL CALLBACK OwnedWindowCollector(HWND hwnd, LPARAM param) {
+ OwnedWindowCollectorContext* context =
+ reinterpret_cast<OwnedWindowCollectorContext*>(param);
+ if (hwnd == context->selected_window()) {
+ // Windows are enumerated in top-down z-order, so we can stop enumerating
+ // upon reaching the selected window.
+ return FALSE;
+ }
+
+ // Skip windows that aren't visible pop-up windows.
+ if (!(GetWindowLong(hwnd, GWL_STYLE) & WS_POPUP) ||
+ !context->window_capture_helper()->IsWindowVisibleOnCurrentDesktop(
+ hwnd)) {
+ return TRUE;
+ }
+
+ // Owned windows that intersect the selected window should be captured.
+ if (context->IsWindowOwnedBySelectedWindow(hwnd) &&
+ context->IsWindowOverlappingSelectedWindow(hwnd)) {
+ // Skip windows that draw shadows around menus. These "SysShadow" windows
+ // would otherwise be captured as solid black bars with no transparency
+ // gradient (since this capturer doesn't detect / respect variations in the
+ // window alpha channel). Any other semi-transparent owned windows will be
+ // captured fully-opaque. This seems preferable to excluding them (at least
+ // when they have content aside from a solid fill color / visual adornment;
+ // e.g. some tooltips have the transparent style set).
+ if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TRANSPARENT) {
+ const WCHAR kSysShadow[] = L"SysShadow";
+ const size_t kClassLength = arraysize(kSysShadow);
+ WCHAR class_name[kClassLength];
+ const int class_name_length =
+ GetClassNameW(hwnd, class_name, kClassLength);
+ if (class_name_length == kClassLength - 1 &&
+ wcscmp(class_name, kSysShadow) == 0) {
+ return TRUE;
+ }
+ }
+
+ context->owned_windows->push_back(hwnd);
+ }
+
+ return TRUE;
+}
+
+WindowCapturerWinGdi::WindowCapturerWinGdi(
+ bool enumerate_current_process_windows)
+ : enumerate_current_process_windows_(enumerate_current_process_windows) {}
+WindowCapturerWinGdi::~WindowCapturerWinGdi() {}
+
+bool WindowCapturerWinGdi::GetSourceList(SourceList* sources) {
+ if (!window_capture_helper_.EnumerateCapturableWindows(
+ sources, enumerate_current_process_windows_))
+ return false;
+
+ std::map<HWND, DesktopSize> new_map;
+ for (const auto& item : *sources) {
+ HWND hwnd = reinterpret_cast<HWND>(item.id);
+ new_map[hwnd] = window_size_map_[hwnd];
+ }
+ window_size_map_.swap(new_map);
+
+ return true;
+}
+
+bool WindowCapturerWinGdi::SelectSource(SourceId id) {
+ HWND window = reinterpret_cast<HWND>(id);
+ if (!IsWindowValidAndVisible(window))
+ return false;
+
+ window_ = window;
+ // When a window is not in the map, window_size_map_[window] will create an
+ // item with DesktopSize (0, 0).
+ previous_size_ = window_size_map_[window];
+ return true;
+}
+
+bool WindowCapturerWinGdi::FocusOnSelectedSource() {
+ if (!window_)
+ return false;
+
+ if (!IsWindowValidAndVisible(window_))
+ return false;
+
+ return BringWindowToTop(window_) && SetForegroundWindow(window_);
+}
+
+bool WindowCapturerWinGdi::IsOccluded(const DesktopVector& pos) {
+ DesktopVector sys_pos = pos.add(GetFullscreenRect().top_left());
+ HWND hwnd =
+ reinterpret_cast<HWND>(window_finder_.GetWindowUnderPoint(sys_pos));
+
+ return hwnd != window_ &&
+ std::find(owned_windows_.begin(), owned_windows_.end(), hwnd) ==
+ owned_windows_.end();
+}
+
+void WindowCapturerWinGdi::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ RecordCapturerImpl(DesktopCapturerId::kWindowCapturerWinGdi);
+
+ callback_ = callback;
+}
+
+void WindowCapturerWinGdi::CaptureFrame() {
+ RTC_DCHECK(callback_);
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ CaptureResults results = CaptureFrame(/*capture_owned_windows*/ true);
+ if (!results.frame) {
+ // Don't return success if we have no frame.
+ results.result = results.result == Result::SUCCESS ? Result::ERROR_TEMPORARY
+ : results.result;
+ callback_->OnCaptureResult(results.result, nullptr);
+ return;
+ }
+
+ int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec;
+ RTC_HISTOGRAM_COUNTS_1000(
+ "WebRTC.DesktopCapture.Win.WindowGdiCapturerFrameTime", capture_time_ms);
+ results.frame->set_capture_time_ms(capture_time_ms);
+ results.frame->set_capturer_id(DesktopCapturerId::kWindowCapturerWinGdi);
+ callback_->OnCaptureResult(results.result, std::move(results.frame));
+}
+
+WindowCapturerWinGdi::CaptureResults WindowCapturerWinGdi::CaptureFrame(
+ bool capture_owned_windows) {
+ TRACE_EVENT0("webrtc", "WindowCapturerWinGdi::CaptureFrame");
+
+ if (!window_) {
+ RTC_LOG(LS_ERROR) << "Window hasn't been selected: " << GetLastError();
+ return {Result::ERROR_PERMANENT, nullptr};
+ }
+
+ // Stop capturing if the window has been closed.
+ if (!IsWindow(window_)) {
+ RTC_LOG(LS_ERROR) << "Target window has been closed.";
+ return {Result::ERROR_PERMANENT, nullptr};
+ }
+
+ // Determine the window region excluding any resize border, and including
+ // any visible border if capturing an owned window / dialog. (Don't include
+ // any visible border for the selected window for consistency with
+ // CroppingWindowCapturerWin, which would expose a bit of the background
+ // through the partially-transparent border.)
+ const bool avoid_cropping_border = !capture_owned_windows;
+ DesktopRect cropped_rect;
+ DesktopRect original_rect;
+
+ if (!GetCroppedWindowRect(window_, avoid_cropping_border, &cropped_rect,
+ &original_rect)) {
+ RTC_LOG(LS_WARNING) << "Failed to get drawable window area: "
+ << GetLastError();
+ return {Result::ERROR_TEMPORARY, nullptr};
+ }
+
+ // Return a 1x1 black frame if the window is minimized or invisible on current
+ // desktop, to match behavior on mace. Window can be temporarily invisible
+ // during the transition of full screen mode on/off.
+ if (original_rect.is_empty() ||
+ !window_capture_helper_.IsWindowVisibleOnCurrentDesktop(window_)) {
+ std::unique_ptr<DesktopFrame> frame(
+ new BasicDesktopFrame(DesktopSize(1, 1)));
+
+ previous_size_ = frame->size();
+ window_size_map_[window_] = previous_size_;
+ return {Result::SUCCESS, std::move(frame)};
+ }
+
+ HDC window_dc = GetWindowDC(window_);
+ if (!window_dc) {
+ RTC_LOG(LS_WARNING) << "Failed to get window DC: " << GetLastError();
+ return {Result::ERROR_TEMPORARY, nullptr};
+ }
+
+ DesktopRect unscaled_cropped_rect = cropped_rect;
+ double horizontal_scale = 1.0;
+ double vertical_scale = 1.0;
+
+ DesktopSize window_dc_size;
+ if (GetDcSize(window_dc, &window_dc_size)) {
+ // The `window_dc_size` is used to detect the scaling of the original
+ // window. If the application does not support high-DPI settings, it will
+ // be scaled by Windows according to the scaling setting.
+ // https://www.google.com/search?q=windows+scaling+settings&ie=UTF-8
+ // So the size of the `window_dc`, i.e. the bitmap we can retrieve from
+ // PrintWindow() or BitBlt() function, will be smaller than
+ // `original_rect` and `cropped_rect`. Part of the captured desktop frame
+ // will be black. See
+ // bug https://bugs.chromium.org/p/webrtc/issues/detail?id=8112 for
+ // details.
+
+ // If `window_dc_size` is smaller than `window_rect`, let's resize both
+ // `original_rect` and `cropped_rect` according to the scaling factor.
+ // This will adjust the width and height of the two rects.
+ horizontal_scale =
+ static_cast<double>(window_dc_size.width()) / original_rect.width();
+ vertical_scale =
+ static_cast<double>(window_dc_size.height()) / original_rect.height();
+ original_rect.Scale(horizontal_scale, vertical_scale);
+ cropped_rect.Scale(horizontal_scale, vertical_scale);
+
+ // Translate `cropped_rect` to the left so that its position within
+ // `original_rect` remains accurate after scaling.
+ // See crbug.com/1083527 for more info.
+ int translate_left = static_cast<int>(std::round(
+ (cropped_rect.left() - original_rect.left()) * (horizontal_scale - 1)));
+ int translate_top = static_cast<int>(std::round(
+ (cropped_rect.top() - original_rect.top()) * (vertical_scale - 1)));
+ cropped_rect.Translate(translate_left, translate_top);
+ }
+
+ std::unique_ptr<DesktopFrameWin> frame(
+ DesktopFrameWin::Create(original_rect.size(), nullptr, window_dc));
+ if (!frame.get()) {
+ RTC_LOG(LS_WARNING) << "Failed to create frame.";
+ ReleaseDC(window_, window_dc);
+ return {Result::ERROR_TEMPORARY, nullptr};
+ }
+
+ HDC mem_dc = CreateCompatibleDC(window_dc);
+ HGDIOBJ previous_object = SelectObject(mem_dc, frame->bitmap());
+ BOOL result = FALSE;
+
+ // When desktop composition (Aero) is enabled each window is rendered to a
+ // private buffer allowing BitBlt() to get the window content even if the
+ // window is occluded. PrintWindow() is slower but lets rendering the window
+ // contents to an off-screen device context when Aero is not available.
+ // PrintWindow() is not supported by some applications.
+ //
+ // If Aero is enabled, we prefer BitBlt() because it's faster and avoids
+ // window flickering. Otherwise, we prefer PrintWindow() because BitBlt() may
+ // render occluding windows on top of the desired window.
+ //
+ // When composition is enabled the DC returned by GetWindowDC() doesn't always
+ // have window frame rendered correctly. Windows renders it only once and then
+ // caches the result between captures. We hack it around by calling
+ // PrintWindow() whenever window size changes, including the first time of
+ // capturing - it somehow affects what we get from BitBlt() on the subsequent
+ // captures.
+ //
+ // For Windows 8.1 and later, we want to always use PrintWindow when the
+ // cropping screen capturer falls back to the window capturer. I.e.
+ // on Windows 8.1 and later, PrintWindow is only used when the window is
+ // occluded. When the window is not occluded, it is much faster to capture
+ // the screen and to crop it to the window position and size.
+ if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN8) {
+ // Special flag that makes PrintWindow to work on Windows 8.1 and later.
+ // Indeed certain apps (e.g. those using DirectComposition rendering) can't
+ // be captured using BitBlt or PrintWindow without this flag. Note that on
+ // Windows 8.0 this flag is not supported so the block below will fallback
+ // to the other call to PrintWindow. It seems to be very tricky to detect
+ // Windows 8.0 vs 8.1 so a try/fallback is more approriate here.
+ const UINT flags = PW_RENDERFULLCONTENT;
+ result = PrintWindow(window_, mem_dc, flags);
+ }
+
+ if (!result && (!window_capture_helper_.IsAeroEnabled() ||
+ !previous_size_.equals(frame->size()))) {
+ result = PrintWindow(window_, mem_dc, 0);
+ }
+
+ // Aero is enabled or PrintWindow() failed, use BitBlt.
+ if (!result) {
+ result = BitBlt(mem_dc, 0, 0, frame->size().width(), frame->size().height(),
+ window_dc, 0, 0, SRCCOPY);
+ }
+
+ SelectObject(mem_dc, previous_object);
+ DeleteDC(mem_dc);
+ ReleaseDC(window_, window_dc);
+
+ previous_size_ = frame->size();
+ window_size_map_[window_] = previous_size_;
+
+ frame->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(frame->size()));
+ frame->set_top_left(
+ original_rect.top_left().subtract(GetFullscreenRect().top_left()));
+
+ if (!result) {
+ RTC_LOG(LS_ERROR) << "Both PrintWindow() and BitBlt() failed.";
+ return {Result::ERROR_TEMPORARY, nullptr};
+ }
+
+ // Rect for the data is relative to the first pixel of the frame.
+ cropped_rect.Translate(-original_rect.left(), -original_rect.top());
+ std::unique_ptr<DesktopFrame> cropped_frame =
+ CreateCroppedDesktopFrame(std::move(frame), cropped_rect);
+ RTC_DCHECK(cropped_frame);
+
+ if (capture_owned_windows) {
+ // If any owned/pop-up windows overlap the selected window, capture them
+ // and copy/composite their contents into the frame.
+ owned_windows_.clear();
+ OwnedWindowCollectorContext context(window_, unscaled_cropped_rect,
+ &window_capture_helper_,
+ &owned_windows_);
+
+ if (context.IsSelectedWindowValid()) {
+ EnumWindows(OwnedWindowCollector, reinterpret_cast<LPARAM>(&context));
+
+ if (!owned_windows_.empty()) {
+ if (!owned_window_capturer_) {
+ owned_window_capturer_ = std::make_unique<WindowCapturerWinGdi>(
+ enumerate_current_process_windows_);
+ }
+
+ // Owned windows are stored in top-down z-order, so this iterates in
+ // reverse to capture / draw them in bottom-up z-order
+ for (auto it = owned_windows_.rbegin(); it != owned_windows_.rend();
+ it++) {
+ HWND hwnd = *it;
+ if (owned_window_capturer_->SelectSource(
+ reinterpret_cast<SourceId>(hwnd))) {
+ CaptureResults results = owned_window_capturer_->CaptureFrame(
+ /*capture_owned_windows*/ false);
+
+ if (results.result != DesktopCapturer::Result::SUCCESS) {
+ // Simply log any error capturing an owned/pop-up window without
+ // bubbling it up to the caller (an expected error here is that
+ // the owned/pop-up window was closed; any unexpected errors won't
+ // fail the outer capture).
+ RTC_LOG(LS_INFO) << "Capturing owned window failed (previous "
+ "error/warning pertained to that)";
+ } else {
+ // Copy / composite the captured frame into the outer frame. This
+ // may no-op if they no longer intersect (if the owned window was
+ // moved outside the owner bounds since scheduled for capture.)
+ cropped_frame->CopyIntersectingPixelsFrom(
+ *results.frame, horizontal_scale, vertical_scale);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return {Result::SUCCESS, std::move(cropped_frame)};
+}
+
+// static
+std::unique_ptr<DesktopCapturer> WindowCapturerWinGdi::CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options) {
+ return std::unique_ptr<DesktopCapturer>(
+ new WindowCapturerWinGdi(options.enumerate_current_process_windows()));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.h b/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.h
new file mode 100644
index 0000000000..bf94dfe192
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 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 MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURER_WIN_GDI_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURER_WIN_GDI_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+#include "modules/desktop_capture/window_finder_win.h"
+
+namespace webrtc {
+
+class WindowCapturerWinGdi : public DesktopCapturer {
+ public:
+ explicit WindowCapturerWinGdi(bool enumerate_current_process_windows);
+
+ // Disallow copy and assign
+ WindowCapturerWinGdi(const WindowCapturerWinGdi&) = delete;
+ WindowCapturerWinGdi& operator=(const WindowCapturerWinGdi&) = delete;
+
+ ~WindowCapturerWinGdi() override;
+
+ static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options);
+
+ // DesktopCapturer interface.
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ bool IsOccluded(const DesktopVector& pos) override;
+
+ private:
+ struct CaptureResults {
+ Result result;
+ std::unique_ptr<DesktopFrame> frame;
+ };
+
+ CaptureResults CaptureFrame(bool capture_owned_windows);
+
+ Callback* callback_ = nullptr;
+
+ // HWND and HDC for the currently selected window or nullptr if window is not
+ // selected.
+ HWND window_ = nullptr;
+
+ DesktopSize previous_size_;
+
+ WindowCaptureHelperWin window_capture_helper_;
+
+ bool enumerate_current_process_windows_;
+
+ // This map is used to avoid flickering for the case when SelectWindow() calls
+ // are interleaved with Capture() calls.
+ std::map<HWND, DesktopSize> window_size_map_;
+
+ WindowFinderWin window_finder_;
+
+ std::vector<HWND> owned_windows_;
+ std::unique_ptr<WindowCapturerWinGdi> owned_window_capturer_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURER_WIN_GDI_H_