fix: use StartUpdating method for PipeWire capturer (#38833)

* fix: use StartUpdating method for PipeWire capturer

Fixed a crash related to PipeWire capturer by adapting to Chromium's
interface changes. Chromium expects a call to
`NativeDesktopMediaList::StartUpdating` with an implementation of
`DesktopMediaListObserver` for delegated capturers like PipeWire. This
interface allows listening to user permission events and listing
sources only after the user has made a choice on the permission dialog.

The interface has been implemented by an inner class to allow listening
to screen and window capture permissions concurrently using two
instances of the class. A patch that was resetting the capturer on the
first refresh has been changed to exclude PipeWire. PipeWire capturer
object will follow the lifecycle of `NativeDesktopMediaList`, as is the
case in Chromium.

Fixes #37463

* fix: wait for thumbnails from PipeWire when necessary

The PipeWire stream starts after the dialog is dismissed. If the sources
are listed immediately afterwards, the thumbnail may not have been
generated by that time. Explicitly wait for both thumbnail generation
and a selection on the source dialog before listing sources.
This commit is contained in:
Athul Iddya 2023-07-11 01:21:11 -07:00 committed by GitHub
parent 3e3152008f
commit 905e41bbdd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 113 additions and 21 deletions

View file

@ -181,6 +181,41 @@ DesktopCapturer::DesktopCapturer(v8::Isolate* isolate) {}
DesktopCapturer::~DesktopCapturer() = default;
DesktopCapturer::DesktopListListener::DesktopListListener(
OnceCallback update_callback,
OnceCallback failure_callback,
bool skip_thumbnails)
: update_callback_(std::move(update_callback)),
failure_callback_(std::move(failure_callback)),
have_thumbnail_(skip_thumbnails) {}
DesktopCapturer::DesktopListListener::~DesktopListListener() = default;
void DesktopCapturer::DesktopListListener::OnDelegatedSourceListSelection() {
if (have_thumbnail_) {
std::move(update_callback_).Run();
} else {
have_selection_ = true;
}
}
void DesktopCapturer::DesktopListListener::OnSourceThumbnailChanged(int index) {
if (have_selection_) {
// This is called every time a thumbnail is refreshed. Reset variable to
// ensure that the callback is not run again.
have_selection_ = false;
// PipeWire returns a single source, so index is not relevant.
std::move(update_callback_).Run();
} else {
have_thumbnail_ = true;
}
}
void DesktopCapturer::DesktopListListener::OnDelegatedSourceListDismissed() {
std::move(failure_callback_).Run();
}
void DesktopCapturer::StartHandling(bool capture_window,
bool capture_screen,
const gfx::Size& thumbnail_size,
@ -212,11 +247,22 @@ void DesktopCapturer::StartHandling(bool capture_window,
window_capturer_ = std::make_unique<NativeDesktopMediaList>(
DesktopMediaList::Type::kWindow, std::move(capturer));
window_capturer_->SetThumbnailSize(thumbnail_size);
window_capturer_->Update(
base::BindOnce(&DesktopCapturer::UpdateSourcesList,
weak_ptr_factory_.GetWeakPtr(),
window_capturer_.get()),
/* refresh_thumbnails = */ true);
OnceCallback update_callback = base::BindOnce(
&DesktopCapturer::UpdateSourcesList, weak_ptr_factory_.GetWeakPtr(),
window_capturer_.get());
if (window_capturer_->IsSourceListDelegated()) {
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());
} else {
window_capturer_->Update(std::move(update_callback),
/* refresh_thumbnails = */ true);
}
}
}
@ -226,11 +272,22 @@ void DesktopCapturer::StartHandling(bool capture_window,
screen_capturer_ = std::make_unique<NativeDesktopMediaList>(
DesktopMediaList::Type::kScreen, std::move(capturer));
screen_capturer_->SetThumbnailSize(thumbnail_size);
screen_capturer_->Update(
base::BindOnce(&DesktopCapturer::UpdateSourcesList,
weak_ptr_factory_.GetWeakPtr(),
screen_capturer_.get()),
/* refresh_thumbnails = */ true);
OnceCallback update_callback = base::BindOnce(
&DesktopCapturer::UpdateSourcesList, weak_ptr_factory_.GetWeakPtr(),
screen_capturer_.get());
if (screen_capturer_->IsSourceListDelegated()) {
OnceCallback failure_callback = base::BindOnce(
&DesktopCapturer::HandleFailure, weak_ptr_factory_.GetWeakPtr());
screen_listener_ = std::make_unique<DesktopListListener>(
std::move(update_callback), std::move(failure_callback),
thumbnail_size.IsEmpty());
screen_capturer_->StartUpdating(screen_listener_.get());
} else {
screen_capturer_->Update(std::move(update_callback),
/* refresh_thumbnails = */ true);
}
}
}
}
@ -269,12 +326,7 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
// |media_list_sources|.
if (!webrtc::DxgiDuplicatorController::Instance()->GetDeviceNames(
&device_names)) {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate);
gin_helper::CallMethod(this, "_onerror", "Failed to get sources.");
Unpin();
HandleFailure();
return;
}
@ -324,6 +376,14 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
}
}
void DesktopCapturer::HandleFailure() {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::HandleScope scope(isolate);
gin_helper::CallMethod(this, "_onerror", "Failed to get sources.");
Unpin();
}
// static
gin::Handle<DesktopCapturer> DesktopCapturer::Create(v8::Isolate* isolate) {
auto handle = gin::CreateHandle(isolate, new DesktopCapturer(isolate));