fix: use generic capturer to list both screens and windows when possible (#39189)

* fix: use generic capturer to list both screens and windows when possible

Screensharing with PipeWire via XDG Desktop Portal requires explicit
user permission via permission dialogs. Chromium has separate tabs for
screens and windows and thus its portal implementation requests
permissions separately for each. However, the screencast portal has no
such limitation and supports both screens and windows in a single
request.

WebRTC now supports this type of capture in a new method called
called `CreateGenericCapturer`. The `desktopCapturer` implementation has
been modified to use it. Additionally, Chromium has been patched to use
same generic capturer to ensure that the source IDs remain valid for
`getUserMedia`.

Co-authored-by: Athul Iddya <athul@iddya.com>

* build: clean up incorrectly backported patches

* chore: cherry-pick 0e9556a90cec from webrtc

---------

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Athul Iddya <athul@iddya.com>
Co-authored-by: Keeley Hammond <vertedinde@electronjs.org>
Co-authored-by: John Kleinschmidt <jkleinsc@electronjs.org>
This commit is contained in:
trop[bot]
2023-07-26 18:45:41 +02:00
committed by GitHub
parent 6b61917d4b
commit bceaa7d4dc
5 changed files with 185 additions and 0 deletions

View File

@@ -131,3 +131,4 @@ fix_crash_on_nativetheme_change_during_context_menu_close.patch
potential_fix_for_flaky_desktopcaptureapitest_delegation_unittest.patch
fix_select_the_first_menu_item_when_opened_via_keyboard.patch
chore_add_buildflag_guard_around_new_include.patch
fix_use_delegated_generic_capturer_when_available.patch

View File

@@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Athul Iddya <athul@iddya.com>
Date: Fri, 14 Jul 2023 08:03:37 -0700
Subject: fix: use delegated generic capturer when available
When the generic capturer is used to fetch capture sources, the returned
ID will be arbitrarily prefixed with "screen" or "window" regardless of
the source type. If the window capturer is used to stream video when the
source was a screen or vice-versa, the stream fails to restart in
delegated capturers like PipeWire.
To fix this, use the generic capturer to fetch the media stream if it's
delegated and available. This does not cause any issues if the original
capturer was window or screen-specific, as the IDs remain valid for
generic capturer as well.
diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc
index 27687ee9f362c708af3e45d61effb20cebb413ae..ac97925378aa3872c7dc55e6184866be855f7a6b 100644
--- a/content/browser/media/capture/desktop_capture_device.cc
+++ b/content/browser/media/capture/desktop_capture_device.cc
@@ -789,8 +789,14 @@ std::unique_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
DesktopCapturerLacros::CaptureType::kScreen,
webrtc::DesktopCaptureOptions());
#else
- std::unique_ptr<webrtc::DesktopCapturer> screen_capturer(
- webrtc::DesktopCapturer::CreateScreenCapturer(options));
+ std::unique_ptr<webrtc::DesktopCapturer> screen_capturer;
+ if (auto generic_capturer =
+ webrtc::DesktopCapturer::CreateGenericCapturer(options);
+ generic_capturer && generic_capturer->GetDelegatedSourceListController()) {
+ screen_capturer = std::move(generic_capturer);
+ } else {
+ screen_capturer = webrtc::DesktopCapturer::CreateScreenCapturer(options);
+ }
#endif
if (screen_capturer && screen_capturer->SelectSource(source.id)) {
capturer = std::make_unique<webrtc::DesktopAndCursorComposer>(
@@ -809,8 +815,14 @@ std::unique_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
new DesktopCapturerLacros(DesktopCapturerLacros::CaptureType::kWindow,
webrtc::DesktopCaptureOptions()));
#else
- std::unique_ptr<webrtc::DesktopCapturer> window_capturer =
- webrtc::DesktopCapturer::CreateWindowCapturer(options);
+ std::unique_ptr<webrtc::DesktopCapturer> window_capturer;
+ if (auto generic_capturer =
+ webrtc::DesktopCapturer::CreateGenericCapturer(options);
+ generic_capturer && generic_capturer->GetDelegatedSourceListController()) {
+ window_capturer = std::move(generic_capturer);
+ } else {
+ window_capturer = webrtc::DesktopCapturer::CreateWindowCapturer(options);
+ }
#endif
if (window_capturer && window_capturer->SelectSource(source.id)) {
capturer = std::make_unique<webrtc::DesktopAndCursorComposer>(

View File

@@ -1,2 +1,3 @@
fix_fallback_to_x11_capturer_on_wayland.patch
cherry-pick-0e9556a90cec.patch
fix_mark_pipewire_capturer_as_failed_after_session_is_closed.patch

View File

@@ -0,0 +1,102 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jan Grulich <grulja@gmail.com>
Date: Wed, 12 Jul 2023 13:15:40 +0200
Subject: Desktop capture: introduce capturer requesting both screen and
windows
When PipeWire and xdg-desktop-portals are used, we can actually combine
both source types into one request. Make this part of the API for those
who want to use it this way, e.g. Firefox or Electron, otherwise they
will end up making two simultaneous requests, resulting into two dialogs
at the same time asking, while they can be combined into just one.
Bug: webrtc:15363
Change-Id: Ib6e1e47f66cb01d5c65096aec378b44c3af5f387
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/311549
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Jan Grulich <grulja@gmail.com>
Cr-Commit-Position: refs/heads/main@{#40425}
diff --git a/modules/desktop_capture/desktop_capture_types.h b/modules/desktop_capture/desktop_capture_types.h
index 9627076eea3d1272f87b773ada86be6051f1e224..a4e3e897fde1d3c7bb470449448f30ff6e50caea 100644
--- a/modules/desktop_capture/desktop_capture_types.h
+++ b/modules/desktop_capture/desktop_capture_types.h
@@ -15,7 +15,7 @@
namespace webrtc {
-enum class CaptureType { kWindow, kScreen };
+enum class CaptureType { kWindow, kScreen, kAnyScreenContent };
// Type used to identify windows on the desktop. Values are platform-specific:
// - On Windows: HWND cast to intptr_t.
diff --git a/modules/desktop_capture/desktop_capturer.cc b/modules/desktop_capture/desktop_capturer.cc
index 5211f1acecaba963e1436c1d04aa953853c79eb7..b99f5ecb803ac0ac0e26fbdb3411c77605f33011 100644
--- a/modules/desktop_capture/desktop_capturer.cc
+++ b/modules/desktop_capture/desktop_capturer.cc
@@ -26,6 +26,10 @@
#include "rtc_base/win/windows_version.h"
#endif // defined(RTC_ENABLE_WIN_WGC)
+#if defined(WEBRTC_USE_PIPEWIRE)
+#include "modules/desktop_capture/linux/wayland/base_capturer_pipewire.h"
+#endif
+
namespace webrtc {
void LogDesktopCapturerFullscreenDetectorUsage() {
@@ -101,6 +105,25 @@ std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateScreenCapturer(
return capturer;
}
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateGenericCapturer(
+ const DesktopCaptureOptions& options) {
+ std::unique_ptr<DesktopCapturer> capturer;
+
+#if defined(WEBRTC_USE_PIPEWIRE)
+ if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) {
+ capturer = std::make_unique<BaseCapturerPipeWire>(
+ options, CaptureType::kAnyScreenContent);
+ }
+
+ if (capturer && options.detect_updated_region()) {
+ capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
+ }
+#endif // defined(WEBRTC_USE_PIPEWIRE)
+
+ return capturer;
+}
+
#if defined(WEBRTC_USE_PIPEWIRE) || defined(WEBRTC_USE_X11)
bool DesktopCapturer::IsRunningUnderWayland() {
const char* xdg_session_type = getenv("XDG_SESSION_TYPE");
diff --git a/modules/desktop_capture/desktop_capturer.h b/modules/desktop_capture/desktop_capturer.h
index fd884f13ff771de2d5ea8b79ba530e9d3b03e913..9c7ecc78f46ea2e2c173416132318f0526714265 100644
--- a/modules/desktop_capture/desktop_capturer.h
+++ b/modules/desktop_capture/desktop_capturer.h
@@ -186,6 +186,11 @@ class RTC_EXPORT DesktopCapturer {
static std::unique_ptr<DesktopCapturer> CreateScreenCapturer(
const DesktopCaptureOptions& options);
+ // Creates a DesktopCapturer instance which targets to capture windows and
+ // screens.
+ static std::unique_ptr<DesktopCapturer> CreateGenericCapturer(
+ const DesktopCaptureOptions& options);
+
#if defined(WEBRTC_USE_PIPEWIRE) || defined(WEBRTC_USE_X11)
static bool IsRunningUnderWayland();
diff --git a/modules/desktop_capture/linux/wayland/screencast_portal.cc b/modules/desktop_capture/linux/wayland/screencast_portal.cc
index a473802176a649a62f6d6a377f97daaa9d1e893c..61ed84ebb532521ce9a4af69355807b04f228859 100644
--- a/modules/desktop_capture/linux/wayland/screencast_portal.cc
+++ b/modules/desktop_capture/linux/wayland/screencast_portal.cc
@@ -41,6 +41,8 @@ ScreenCastPortal::CaptureSourceType ScreenCastPortal::ToCaptureSourceType(
return ScreenCastPortal::CaptureSourceType::kScreen;
case CaptureType::kWindow:
return ScreenCastPortal::CaptureSourceType::kWindow;
+ case CaptureType::kAnyScreenContent:
+ return ScreenCastPortal::CaptureSourceType::kAnyScreenContent;
}
}

View File

@@ -234,6 +234,33 @@ void DesktopCapturer::StartHandling(bool capture_window,
// clear any existing captured sources.
captured_sources_.clear();
if (capture_window && capture_screen) {
// Some capturers like PipeWire suppport a single capturer for both screens
// and windows. Use it if possible, treating both as window capture
if (auto capturer = webrtc::DesktopCapturer::CreateGenericCapturer(
content::desktop_capture::CreateDesktopCaptureOptions());
capturer && capturer->GetDelegatedSourceListController()) {
capture_screen_ = false;
capture_window_ = capture_window;
window_capturer_ = std::make_unique<NativeDesktopMediaList>(
DesktopMediaList::Type::kWindow, std::move(capturer));
window_capturer_->SetThumbnailSize(thumbnail_size);
OnceCallback update_callback = base::BindOnce(
&DesktopCapturer::UpdateSourcesList, weak_ptr_factory_.GetWeakPtr(),
window_capturer_.get());
OnceCallback failure_callback = base::BindOnce(
&DesktopCapturer::HandleFailure, weak_ptr_factory_.GetWeakPtr());
window_listener_ = std::make_unique<DesktopListListener>(
std::move(update_callback), std::move(failure_callback),
thumbnail_size.IsEmpty());
window_capturer_->StartUpdating(window_listener_.get());
return;
}
}
// Start listening for captured sources.
capture_window_ = capture_window;
capture_screen_ = capture_screen;