Wayland screensharing: implement stream restoration

Make use of "persist_mode" option in ScreenCast portal to restore
previously selected screen/window and avoid picking it again in yet
another xdg-desktop-portal dialog.

Bug: webrtc:13429
Change-Id: I3a0068091c2dd38003a7dff3f82b9cdb2ccd0f42
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/263901
Commit-Queue: Jan Grulich <grulja@gmail.com>
Reviewed-by: Alexander Cooper <alcooper@chromium.org>
Cr-Commit-Position: refs/heads/main@{#37257}
This commit is contained in:
Jan Grulich 2022-06-06 19:59:39 +02:00 committed by WebRTC LUCI CQ
parent ef159280b1
commit 93f9db7e8a
7 changed files with 184 additions and 6 deletions

View File

@ -622,6 +622,8 @@ rtc_library("desktop_capture_generic") {
"linux/wayland/mouse_cursor_monitor_pipewire.cc",
"linux/wayland/mouse_cursor_monitor_pipewire.h",
"linux/wayland/portal_request_response.h",
"linux/wayland/restore_token_manager.cc",
"linux/wayland/restore_token_manager.h",
"linux/wayland/scoped_glib.cc",
"linux/wayland/scoped_glib.h",
"linux/wayland/screen_capture_portal_interface.cc",

View File

@ -12,9 +12,12 @@
#include "modules/desktop_capture/desktop_capture_options.h"
#include "modules/desktop_capture/desktop_capturer.h"
#include "modules/desktop_capture/linux/wayland/restore_token_manager.h"
#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
#include "rtc_base/checks.h"
#include "rtc_base/logging.h"
#include "rtc_base/random.h"
#include "rtc_base/time_utils.h"
namespace webrtc {
@ -31,12 +34,19 @@ BaseCapturerPipeWire::BaseCapturerPipeWire(const DesktopCaptureOptions& options)
options,
std::make_unique<ScreenCastPortal>(
ScreenCastPortal::CaptureSourceType::kAnyScreenContent,
this)) {}
this)) {
is_screencast_portal_ = true;
}
BaseCapturerPipeWire::BaseCapturerPipeWire(
const DesktopCaptureOptions& options,
std::unique_ptr<ScreenCapturePortalInterface> portal)
: options_(options), portal_(std::move(portal)) {}
: options_(options),
is_screencast_portal_(false),
portal_(std::move(portal)) {
Random random(rtc::TimeMicros());
source_id_ = static_cast<SourceId>(random.Rand(1, INT_MAX));
}
BaseCapturerPipeWire::~BaseCapturerPipeWire() {}
@ -49,6 +59,11 @@ void BaseCapturerPipeWire::OnScreenCastRequestResult(RequestResponse result,
capturer_failed_ = true;
RTC_LOG(LS_ERROR) << "ScreenCastPortal failed: "
<< static_cast<uint>(result);
} else if (ScreenCastPortal* screencast_portal = GetScreenCastPortal()) {
if (!screencast_portal->RestoreToken().empty()) {
RestoreTokenManager::GetInstance().AddToken(
source_id_, screencast_portal->RestoreToken());
}
}
}
@ -71,6 +86,15 @@ void BaseCapturerPipeWire::Start(Callback* callback) {
callback_ = callback;
if (ScreenCastPortal* screencast_portal = GetScreenCastPortal()) {
screencast_portal->SetPersistMode(
ScreenCastPortal::PersistMode::kTransient);
if (selected_source_id_) {
screencast_portal->SetRestoreToken(
RestoreTokenManager::GetInstance().TakeToken(selected_source_id_));
}
}
portal_->Start();
}
@ -103,12 +127,13 @@ bool BaseCapturerPipeWire::GetSourceList(SourceList* sources) {
// is often treated as a null/placeholder id, so we shouldn't use that.
// TODO(https://crbug.com/1297671): Reconsider type of ID when plumbing
// token that will enable stream re-use.
sources->push_back({1});
sources->push_back({source_id_});
return true;
}
bool BaseCapturerPipeWire::SelectSource(SourceId id) {
// Screen selection is handled by the xdg-desktop-portal.
selected_source_id_ = id;
return true;
}
@ -116,4 +141,9 @@ SessionDetails BaseCapturerPipeWire::GetSessionDetails() {
return portal_->GetSessionDetails();
}
ScreenCastPortal* BaseCapturerPipeWire::GetScreenCastPortal() {
return is_screencast_portal_ ? static_cast<ScreenCastPortal*>(portal_.get())
: nullptr;
}
} // namespace webrtc

View File

@ -50,9 +50,22 @@ class BaseCapturerPipeWire : public DesktopCapturer,
xdg_portal::SessionDetails GetSessionDetails();
private:
ScreenCastPortal* GetScreenCastPortal();
DesktopCaptureOptions options_ = {};
Callback* callback_ = nullptr;
bool capturer_failed_ = false;
bool is_screencast_portal_ = false;
// SourceId that is selected using SelectSource() and that we previously
// returned in GetSourceList(). This should be a SourceId that has a restore
// token associated with it and can be restored if we have required version
// of xdg-desktop-portal.
SourceId selected_source_id_ = 0;
// SourceID we randomly generate and that is returned in GetSourceList() as
// available source that will later get assigned to a restore token in order
// to be restored later using SelectSource().
SourceId source_id_ = 0;
std::unique_ptr<xdg_portal::ScreenCapturePortalInterface> portal_;
};

View File

@ -0,0 +1,33 @@
/*
* Copyright 2022 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/linux/wayland/restore_token_manager.h"
namespace webrtc {
// static
RestoreTokenManager& RestoreTokenManager::GetInstance() {
static webrtc::RestoreTokenManager* manager = new RestoreTokenManager();
return *manager;
}
void RestoreTokenManager::AddToken(DesktopCapturer::SourceId id,
const std::string& token) {
restore_tokens_.insert({id, token});
}
std::string RestoreTokenManager::TakeToken(DesktopCapturer::SourceId id) {
std::string token = restore_tokens_[id];
// Remove the token as it cannot be used anymore
restore_tokens_.erase(id);
return token;
}
} // namespace webrtc

View File

@ -0,0 +1,41 @@
/*
* Copyright 2022 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_LINUX_WAYLAND_RESTORE_TOKEN_MANAGER_H_
#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_RESTORE_TOKEN_MANAGER_H_
#include <mutex>
#include <string>
#include <unordered_map>
#include "modules/desktop_capture/desktop_capturer.h"
namespace webrtc {
class RestoreTokenManager {
public:
RestoreTokenManager(const RestoreTokenManager& manager) = delete;
RestoreTokenManager& operator=(const RestoreTokenManager& manager) = delete;
static RestoreTokenManager& GetInstance();
void AddToken(DesktopCapturer::SourceId id, const std::string& token);
std::string TakeToken(DesktopCapturer::SourceId id);
private:
RestoreTokenManager() = default;
~RestoreTokenManager() = default;
std::unordered_map<DesktopCapturer::SourceId, std::string> restore_tokens_;
};
} // namespace webrtc
#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_RESTORE_TOKEN_MANAGER_H_

View File

@ -198,11 +198,11 @@ void ScreenCastPortal::SourcesRequest() {
g_variant_builder_add(&builder, "{sv}", "multiple",
g_variant_new_boolean(false));
Scoped<GVariant> variant(
Scoped<GVariant> cursorModesVariant(
g_dbus_proxy_get_cached_property(proxy_, "AvailableCursorModes"));
if (variant.get()) {
if (cursorModesVariant.get()) {
uint32_t modes = 0;
g_variant_get(variant.get(), "u", &modes);
g_variant_get(cursorModesVariant.get(), "u", &modes);
// Make request only if this mode is advertised by the portal
// implementation.
if (modes & static_cast<uint32_t>(cursor_mode_)) {
@ -212,6 +212,23 @@ void ScreenCastPortal::SourcesRequest() {
}
}
Scoped<GVariant> versionVariant(
g_dbus_proxy_get_cached_property(proxy_, "version"));
if (versionVariant.get()) {
uint32_t version = 0;
g_variant_get(versionVariant.get(), "u", &version);
// Make request only if xdg-desktop-portal has required API version
if (version >= 4) {
g_variant_builder_add(
&builder, "{sv}", "persist_mode",
g_variant_new_uint32(static_cast<uint32_t>(persist_mode_)));
if (!restore_token_.empty()) {
g_variant_builder_add(&builder, "{sv}", "restore_token",
g_variant_new_string(restore_token_.c_str()));
}
}
}
variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT));
g_variant_builder_add(&builder, "{sv}", "handle_token",
g_variant_new_string(variant_string.get()));
@ -320,6 +337,7 @@ void ScreenCastPortal::OnStartRequestResponseSignal(GDBusConnection* connection,
uint32_t portal_response;
Scoped<GVariant> response_data;
Scoped<GVariantIter> iter;
Scoped<char> restore_token;
g_variant_get(parameters, "(u@a{sv})", &portal_response,
response_data.receive());
if (portal_response || !response_data) {
@ -354,6 +372,11 @@ void ScreenCastPortal::OnStartRequestResponseSignal(GDBusConnection* connection,
}
}
if (g_variant_lookup(response_data.get(), "restore_token", "s",
restore_token.receive())) {
that->restore_token_ = restore_token.get();
}
that->OpenPipeWireRemote();
}
@ -361,6 +384,18 @@ uint32_t ScreenCastPortal::pipewire_stream_node_id() {
return pw_stream_node_id_;
}
void ScreenCastPortal::SetPersistMode(ScreenCastPortal::PersistMode mode) {
persist_mode_ = mode;
}
void ScreenCastPortal::SetRestoreToken(const std::string& token) {
restore_token_ = token;
}
std::string ScreenCastPortal::RestoreToken() const {
return restore_token_;
}
void ScreenCastPortal::OpenPipeWireRemote() {
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);

View File

@ -58,6 +58,21 @@ class ScreenCastPortal : public xdg_portal::ScreenCapturePortalInterface {
kMetadata = 0b100
};
// Values are set based on persist mode property in
// xdg-desktop-portal/screencast
// https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml
enum class PersistMode : uint32_t {
// Do not allow to restore stream
kDoNotPersist = 0b00,
// The restore token is valid as long as the application is alive. It's
// stored in memory and revoked when the application closes its DBus
// connection
kTransient = 0b01,
// The restore token is stored in disk and is valid until the user manually
// revokes it
kPersistent = 0b10
};
// Interface that must be implemented by the ScreenCastPortal consumers.
class PortalNotifier {
public:
@ -106,6 +121,11 @@ class ScreenCastPortal : public xdg_portal::ScreenCapturePortalInterface {
void SourcesRequest();
void OpenPipeWireRemote();
// ScreenCast specific methods for stream restoration
void SetPersistMode(ScreenCastPortal::PersistMode mode);
void SetRestoreToken(const std::string& token);
std::string RestoreToken() const;
private:
PortalNotifier* notifier_;
@ -113,12 +133,16 @@ class ScreenCastPortal : public xdg_portal::ScreenCapturePortalInterface {
uint32_t pw_stream_node_id_ = 0;
// A file descriptor of PipeWire socket
int pw_fd_ = -1;
// Restore token that can be used to restore previous session
std::string restore_token_;
CaptureSourceType capture_source_type_ =
ScreenCastPortal::CaptureSourceType::kScreen;
CursorMode cursor_mode_ = ScreenCastPortal::CursorMode::kMetadata;
PersistMode persist_mode_ = ScreenCastPortal::PersistMode::kDoNotPersist;
ProxyRequestResponseHandler proxy_request_response_handler_;
SourcesRequestResponseSignalHandler sources_request_response_signal_handler_;
gpointer user_data_;