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:
parent
ef159280b1
commit
93f9db7e8a
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
@ -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_
|
||||
@ -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);
|
||||
|
||||
@ -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_;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user