Reland "Add plumbing to control PipeWire picker visibility"

This reverts commit 0098a441e3990f80bcbe05dfce8c6e2359f7ef25.

Reason for revert: Fix chromium build break

Original change's description:
> Revert "Add plumbing to control PipeWire picker visibility"
>
> This reverts commit fbea8c519684577a38cb35b9287ba4645a905094.
>
> Reason for revert: Breaks WebRTC import into Chromium, e.g:
> https://chromium-review.googlesource.com/c/chromium/src/+/3863998/
>
> Original change's description:
> > 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}
>
> Bug: chromium:1351572
> Change-Id: I06f76ab9c8bc1aa303dae177d48698951fdc5ecd
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/273703
> Auto-Submit: Henrik Boström <hbos@webrtc.org>
> Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
> Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>
> Reviewed-by: Henrik Boström <hbos@webrtc.org>
> Commit-Queue: Henrik Andreassson <henrika@webrtc.org>
> Cr-Commit-Position: refs/heads/main@{#37964}

Bug: chromium:1351572
Change-Id: I9e5e691746b81517bf0e211d0ad5a23371ab29ba
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/273685
Reviewed-by: Mark Foltz <mfoltz@chromium.org>
Auto-Submit: Alexander Cooper <alcooper@chromium.org>
Commit-Queue: Mark Foltz <mfoltz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#37972}
This commit is contained in:
Alex Cooper 2022-08-31 10:31:11 -07:00 committed by WebRTC LUCI CQ
parent 813177d98f
commit 86015a6610
10 changed files with 160 additions and 28 deletions

View File

@ -29,6 +29,11 @@ namespace webrtc {
DesktopCapturer::~DesktopCapturer() = default;
DelegatedSourceListController*
DesktopCapturer::GetDelegatedSourceListController() {
return nullptr;
}
void DesktopCapturer::SetSharedMemoryFactory(
std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {}

View File

@ -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.

View File

@ -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_->Stop();
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_->Stop();
}
SessionDetails BaseCapturerPipeWire::GetSessionDetails() {
return portal_->GetSessionDetails();
}

View File

@ -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

View File

@ -30,4 +30,8 @@ std::string RestoreTokenManager::TakeToken(DesktopCapturer::SourceId id) {
return token;
}
DesktopCapturer::SourceId RestoreTokenManager::GetUnusedId() {
return ++last_source_id_;
}
} // namespace webrtc

View File

@ -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_;
};

View File

@ -42,6 +42,10 @@ class ScreenCapturePortalInterface {
virtual xdg_portal::SessionDetails GetSessionDetails() { return {}; }
// Starts the portal setup.
virtual void Start() {}
// Stops and cleans up the portal.
virtual void Stop() {}
// Notifies observers about the success/fail state of the portal
// request/response.
virtual void OnPortalDone(xdg_portal::RequestResponse result) {}

View File

@ -56,19 +56,21 @@ ScreenCastPortal::ScreenCastPortal(
user_data_(user_data) {}
ScreenCastPortal::~ScreenCastPortal() {
Cleanup();
Stop();
}
void ScreenCastPortal::Cleanup() {
void ScreenCastPortal::Stop() {
UnsubscribeSignalHandlers();
TearDownSession(std::move(session_handle_), proxy_, cancellable_,
connection_);
session_handle_ = "";
cancellable_ = nullptr;
proxy_ = nullptr;
restore_token_ = "";
if (pw_fd_ != -1) {
close(pw_fd_);
pw_fd_ = -1;
}
}
@ -121,7 +123,7 @@ xdg_portal::SessionDetails ScreenCastPortal::GetSessionDetails() {
void ScreenCastPortal::OnPortalDone(RequestResponse result) {
notifier_->OnScreenCastRequestResult(result, pw_stream_node_id_, pw_fd_);
if (result != RequestResponse::kSuccess) {
Cleanup();
Stop();
}
}

View File

@ -105,6 +105,7 @@ class ScreenCastPortal : public xdg_portal::ScreenCapturePortalInterface {
// was successful and only then you will be able to get all the required
// information in order to continue working with PipeWire.
void Start() override;
void Stop() override;
xdg_portal::SessionDetails GetSessionDetails() override;
// Method to notify the reason for failure of a portal request.
@ -112,7 +113,6 @@ class ScreenCastPortal : public xdg_portal::ScreenCapturePortalInterface {
// Sends a create session request to the portal.
void RequestSession(GDBusProxy* proxy) override;
void Cleanup();
// Set of methods leveraged by remote desktop portal to setup a common session
// with screen cast portal.

View File

@ -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_ = {};
@ -363,25 +366,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")
@ -550,15 +535,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>{};
}