Add plumbing to control PipeWire picker visibility
Introduces the notion of a "delegated source list" and corresponding controller. This is used by desktop capturers (currently just the PipeWire capturer), who control selecting the source through their own (often system-level) UI, rather than returning a source list with all available options that can then be selected by the embedder. Adds a method to get the controller which serves to also tell embedders if the capturer makes use of a delegated source list. The controller currently allows the embedder to request that the delegated source list be shown or hidden, and will in the future be used to expose events from the source list (e.g. selection, dismissal, error). Bug: chromium:1351572 Change-Id: Ie1d36ed654013f59b8d9095deef01a4705fd5bde Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/272621 Reviewed-by: Mark Foltz <mfoltz@chromium.org> Commit-Queue: Alexander Cooper <alcooper@chromium.org> Cr-Commit-Position: refs/heads/main@{#37956}
This commit is contained in:

committed by
WebRTC LUCI CQ

parent
a9d9820928
commit
fbea8c5196
@ -29,6 +29,11 @@ namespace webrtc {
|
||||
|
||||
DesktopCapturer::~DesktopCapturer() = default;
|
||||
|
||||
DelegatedSourceListController*
|
||||
DesktopCapturer::GetDelegatedSourceListController() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DesktopCapturer::SetSharedMemoryFactory(
|
||||
std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {}
|
||||
|
||||
|
@ -32,6 +32,31 @@ namespace webrtc {
|
||||
class DesktopCaptureOptions;
|
||||
class DesktopFrame;
|
||||
|
||||
// A controller to be implemented and returned by
|
||||
// GetDelegatedSourceListController in capturers that require showing their own
|
||||
// source list and managing user selection there. Apart from ensuring the
|
||||
// visibility of the source list, these capturers should largely be interacted
|
||||
// with the same as a normal capturer, though there may be some caveats for
|
||||
// some DesktopCapturer methods. See GetDelegatedSourceListController for more
|
||||
// information.
|
||||
class RTC_EXPORT DelegatedSourceListController {
|
||||
public:
|
||||
// Used to prompt the capturer to show the delegated source list. If the
|
||||
// source list is already visible, this will be a no-op. Must be called after
|
||||
// starting the DesktopCapturer.
|
||||
//
|
||||
// Note that any selection from a previous invocation of the source list may
|
||||
// be cleared when this method is called.
|
||||
virtual void EnsureVisible() = 0;
|
||||
|
||||
// Used to prompt the capturer to hide the delegated source list. If the
|
||||
// source list is already hidden, this will be a no-op.
|
||||
virtual void EnsureHidden() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~DelegatedSourceListController() {}
|
||||
};
|
||||
|
||||
// Abstract interface for screen and window capturers.
|
||||
class RTC_EXPORT DesktopCapturer {
|
||||
public:
|
||||
@ -88,6 +113,18 @@ class RTC_EXPORT DesktopCapturer {
|
||||
// valid until capturer is destroyed.
|
||||
virtual void Start(Callback* callback) = 0;
|
||||
|
||||
// Returns a valid pointer if the capturer requires the user to make a
|
||||
// selection from a source list provided by the capturer.
|
||||
// Returns nullptr if the capturer does not provide a UI for the user to make
|
||||
// a selection.
|
||||
//
|
||||
// Callers should not take ownership of the returned pointer, but it is
|
||||
// guaranteed to be valid as long as the desktop_capturer is valid.
|
||||
// Note that consumers should still use GetSourceList and SelectSource, but
|
||||
// their behavior may be modified if this returns a value. See those methods
|
||||
// for a more in-depth discussion of those potential modifications.
|
||||
virtual DelegatedSourceListController* GetDelegatedSourceListController();
|
||||
|
||||
// Sets SharedMemoryFactory that will be used to create buffers for the
|
||||
// captured frames. The factory can be invoked on a thread other than the one
|
||||
// where CaptureFrame() is called. It will be destroyed on the same thread.
|
||||
@ -116,10 +153,19 @@ class RTC_EXPORT DesktopCapturer {
|
||||
// should return monitors.
|
||||
// For DesktopCapturer implementations to capture windows, this function
|
||||
// should only return root windows owned by applications.
|
||||
//
|
||||
// Note that capturers who use a delegated source list will return a
|
||||
// SourceList with exactly one value, but it may not be viable for capture
|
||||
// (e.g. CaptureFrame will return ERROR_TEMPORARY) until a selection has been
|
||||
// made.
|
||||
virtual bool GetSourceList(SourceList* sources);
|
||||
|
||||
// Selects a source to be captured. Returns false in case of a failure (e.g.
|
||||
// if there is no source with the specified type and id.)
|
||||
//
|
||||
// Note that some capturers with delegated source lists may also support
|
||||
// selecting a SourceID that is not in the returned source list as a form of
|
||||
// restore token.
|
||||
virtual bool SelectSource(SourceId id);
|
||||
|
||||
// Brings the selected source to the front and sets the input focus on it.
|
||||
|
@ -16,8 +16,6 @@
|
||||
#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 {
|
||||
|
||||
@ -44,8 +42,7 @@ BaseCapturerPipeWire::BaseCapturerPipeWire(
|
||||
: options_(options),
|
||||
is_screencast_portal_(false),
|
||||
portal_(std::move(portal)) {
|
||||
Random random(rtc::TimeMicros());
|
||||
source_id_ = static_cast<SourceId>(random.Rand(1, INT_MAX));
|
||||
source_id_ = RestoreTokenManager::GetInstance().GetUnusedId();
|
||||
}
|
||||
|
||||
BaseCapturerPipeWire::~BaseCapturerPipeWire() {}
|
||||
@ -53,6 +50,11 @@ BaseCapturerPipeWire::~BaseCapturerPipeWire() {}
|
||||
void BaseCapturerPipeWire::OnScreenCastRequestResult(RequestResponse result,
|
||||
uint32_t stream_node_id,
|
||||
int fd) {
|
||||
is_portal_open_ = false;
|
||||
|
||||
// Reset the value of capturer_failed_ in case we succeed below. If we fail,
|
||||
// then it'll set it to the right value again soon enough.
|
||||
capturer_failed_ = false;
|
||||
if (result != RequestResponse::kSuccess ||
|
||||
!options_.screencast_stream()->StartScreenCastStream(
|
||||
stream_node_id, fd, options_.get_width(), options_.get_height())) {
|
||||
@ -95,11 +97,15 @@ void BaseCapturerPipeWire::Start(Callback* callback) {
|
||||
}
|
||||
}
|
||||
|
||||
is_portal_open_ = true;
|
||||
portal_->Start();
|
||||
}
|
||||
|
||||
void BaseCapturerPipeWire::CaptureFrame() {
|
||||
if (capturer_failed_) {
|
||||
// This could be recoverable if the source list is re-summoned; but for our
|
||||
// purposes this is fine, since it requires intervention to resolve and
|
||||
// essentially starts a new capture.
|
||||
callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
|
||||
return;
|
||||
}
|
||||
@ -137,6 +143,35 @@ bool BaseCapturerPipeWire::SelectSource(SourceId id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DelegatedSourceListController*
|
||||
BaseCapturerPipeWire::GetDelegatedSourceListController() {
|
||||
return this;
|
||||
}
|
||||
|
||||
void BaseCapturerPipeWire::EnsureVisible() {
|
||||
RTC_DCHECK(callback_);
|
||||
if (is_portal_open_)
|
||||
return;
|
||||
|
||||
// Clear any previously selected state/capture
|
||||
portal_->Cleanup();
|
||||
options_.screencast_stream()->StopScreenCastStream();
|
||||
|
||||
// Get a new source id to reflect that the source has changed.
|
||||
source_id_ = RestoreTokenManager::GetInstance().GetUnusedId();
|
||||
|
||||
is_portal_open_ = true;
|
||||
portal_->Start();
|
||||
}
|
||||
|
||||
void BaseCapturerPipeWire::EnsureHidden() {
|
||||
if (!is_portal_open_)
|
||||
return;
|
||||
|
||||
is_portal_open_ = false;
|
||||
portal_->Cleanup();
|
||||
}
|
||||
|
||||
SessionDetails BaseCapturerPipeWire::GetSessionDetails() {
|
||||
return portal_->GetSessionDetails();
|
||||
}
|
||||
|
@ -23,6 +23,7 @@
|
||||
namespace webrtc {
|
||||
|
||||
class BaseCapturerPipeWire : public DesktopCapturer,
|
||||
public DelegatedSourceListController,
|
||||
public ScreenCastPortal::PortalNotifier {
|
||||
public:
|
||||
explicit BaseCapturerPipeWire(const DesktopCaptureOptions& options);
|
||||
@ -39,6 +40,11 @@ class BaseCapturerPipeWire : public DesktopCapturer,
|
||||
void CaptureFrame() override;
|
||||
bool GetSourceList(SourceList* sources) override;
|
||||
bool SelectSource(SourceId id) override;
|
||||
DelegatedSourceListController* GetDelegatedSourceListController() override;
|
||||
|
||||
// DelegatedSourceListController
|
||||
void EnsureVisible() override;
|
||||
void EnsureHidden() override;
|
||||
|
||||
// ScreenCastPortal::PortalNotifier interface.
|
||||
void OnScreenCastRequestResult(xdg_portal::RequestResponse result,
|
||||
@ -56,6 +62,7 @@ class BaseCapturerPipeWire : public DesktopCapturer,
|
||||
Callback* callback_ = nullptr;
|
||||
bool capturer_failed_ = false;
|
||||
bool is_screencast_portal_ = false;
|
||||
bool is_portal_open_ = false;
|
||||
|
||||
// SourceId that is selected using SelectSource() and that we previously
|
||||
// returned in GetSourceList(). This should be a SourceId that has a restore
|
||||
|
@ -30,4 +30,8 @@ std::string RestoreTokenManager::TakeToken(DesktopCapturer::SourceId id) {
|
||||
return token;
|
||||
}
|
||||
|
||||
DesktopCapturer::SourceId RestoreTokenManager::GetUnusedId() {
|
||||
return ++last_source_id_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -29,10 +29,15 @@ class RestoreTokenManager {
|
||||
void AddToken(DesktopCapturer::SourceId id, const std::string& token);
|
||||
std::string TakeToken(DesktopCapturer::SourceId id);
|
||||
|
||||
// Returns a source ID which does not have any token associated with it yet.
|
||||
DesktopCapturer::SourceId GetUnusedId();
|
||||
|
||||
private:
|
||||
RestoreTokenManager() = default;
|
||||
~RestoreTokenManager() = default;
|
||||
|
||||
DesktopCapturer::SourceId last_source_id_ = 0;
|
||||
|
||||
std::unordered_map<DesktopCapturer::SourceId, std::string> restore_tokens_;
|
||||
};
|
||||
|
||||
|
@ -42,6 +42,9 @@ class ScreenCapturePortalInterface {
|
||||
virtual xdg_portal::SessionDetails GetSessionDetails() { return {}; }
|
||||
// Starts the portal setup.
|
||||
virtual void Start() {}
|
||||
|
||||
virtual void Cleanup() {}
|
||||
|
||||
// Notifies observers about the success/fail state of the portal
|
||||
// request/response.
|
||||
virtual void OnPortalDone(xdg_portal::RequestResponse result) {}
|
||||
|
@ -66,9 +66,11 @@ void ScreenCastPortal::Cleanup() {
|
||||
session_handle_ = "";
|
||||
cancellable_ = nullptr;
|
||||
proxy_ = nullptr;
|
||||
restore_token_ = "";
|
||||
|
||||
if (pw_fd_ != -1) {
|
||||
close(pw_fd_);
|
||||
pw_fd_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,7 @@ class ScreenCastPortal : public xdg_portal::ScreenCapturePortalInterface {
|
||||
|
||||
// Sends a create session request to the portal.
|
||||
void RequestSession(GDBusProxy* proxy) override;
|
||||
void Cleanup();
|
||||
void Cleanup() override;
|
||||
|
||||
// Set of methods leveraged by remote desktop portal to setup a common session
|
||||
// with screen cast portal.
|
||||
|
@ -98,6 +98,9 @@ class SharedScreenCastStreamPrivate {
|
||||
DesktopVector CaptureCursorPosition();
|
||||
|
||||
private:
|
||||
// Stops the streams and cleans up any in-use elements.
|
||||
void StopAndCleanupStream();
|
||||
|
||||
uint32_t pw_stream_node_id_ = 0;
|
||||
|
||||
DesktopSize stream_size_ = {};
|
||||
@ -362,25 +365,7 @@ void SharedScreenCastStreamPrivate::OnRenegotiateFormat(void* data, uint64_t) {
|
||||
SharedScreenCastStreamPrivate::SharedScreenCastStreamPrivate() {}
|
||||
|
||||
SharedScreenCastStreamPrivate::~SharedScreenCastStreamPrivate() {
|
||||
if (pw_main_loop_) {
|
||||
pw_thread_loop_stop(pw_main_loop_);
|
||||
}
|
||||
|
||||
if (pw_stream_) {
|
||||
pw_stream_destroy(pw_stream_);
|
||||
}
|
||||
|
||||
if (pw_core_) {
|
||||
pw_core_disconnect(pw_core_);
|
||||
}
|
||||
|
||||
if (pw_context_) {
|
||||
pw_context_destroy(pw_context_);
|
||||
}
|
||||
|
||||
if (pw_main_loop_) {
|
||||
pw_thread_loop_destroy(pw_main_loop_);
|
||||
}
|
||||
StopAndCleanupStream();
|
||||
}
|
||||
|
||||
RTC_NO_SANITIZE("cfi-icall")
|
||||
@ -549,15 +534,54 @@ void SharedScreenCastStreamPrivate::UpdateScreenCastStreamResolution(
|
||||
}
|
||||
|
||||
void SharedScreenCastStreamPrivate::StopScreenCastStream() {
|
||||
StopAndCleanupStream();
|
||||
}
|
||||
|
||||
void SharedScreenCastStreamPrivate::StopAndCleanupStream() {
|
||||
// We get buffers on the PipeWire thread, but this is called from the capturer
|
||||
// thread, so we need to wait on and stop the pipewire thread before we
|
||||
// disconnect the stream so that we can guarantee we aren't in the middle of
|
||||
// processing a new frame.
|
||||
|
||||
// Even if we *do* somehow have the other objects without a pipewire thread,
|
||||
// destroying them without a thread causes a crash.
|
||||
if (!pw_main_loop_)
|
||||
return;
|
||||
|
||||
// While we can stop the thread now, we cannot destroy it until we've cleaned
|
||||
// up the other members.
|
||||
pw_thread_loop_wait(pw_main_loop_);
|
||||
pw_thread_loop_stop(pw_main_loop_);
|
||||
|
||||
if (pw_stream_) {
|
||||
pw_stream_disconnect(pw_stream_);
|
||||
pw_stream_destroy(pw_stream_);
|
||||
pw_stream_ = nullptr;
|
||||
|
||||
{
|
||||
webrtc::MutexLock lock(&queue_lock_);
|
||||
queue_.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (pw_core_) {
|
||||
pw_core_disconnect(pw_core_);
|
||||
pw_core_ = nullptr;
|
||||
}
|
||||
|
||||
if (pw_context_) {
|
||||
pw_context_destroy(pw_context_);
|
||||
pw_context_ = nullptr;
|
||||
}
|
||||
|
||||
pw_thread_loop_destroy(pw_main_loop_);
|
||||
pw_main_loop_ = nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<DesktopFrame> SharedScreenCastStreamPrivate::CaptureFrame() {
|
||||
webrtc::MutexLock lock(&queue_lock_);
|
||||
|
||||
if (!queue_.current_frame()) {
|
||||
if (!pw_stream_ || !queue_.current_frame()) {
|
||||
return std::unique_ptr<DesktopFrame>{};
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user