diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc')
-rw-r--r-- | third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc b/third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc new file mode 100644 index 0000000000..85b9f20228 --- /dev/null +++ b/third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2023 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/video_capture/linux/camera_portal.h" + +#include <gio/gio.h> +#include <gio/gunixfdlist.h> + +#include "modules/portal/pipewire_utils.h" +#include "modules/portal/xdg_desktop_portal_utils.h" + +namespace webrtc { + +using xdg_portal::RequestResponse; +using xdg_portal::RequestResponseFromPortalResponse; +using xdg_portal::RequestSessionProxy; + +constexpr char kCameraInterfaceName[] = "org.freedesktop.portal.Camera"; + +class CameraPortalPrivate { + public: + explicit CameraPortalPrivate(CameraPortal::PortalNotifier* notifier); + ~CameraPortalPrivate(); + + void Start(); + + private: + void OnPortalDone(xdg_portal::RequestResponse result, + int fd = kInvalidPipeWireFd); + + static void OnProxyRequested(GObject* object, + GAsyncResult* result, + gpointer user_data); + void ProxyRequested(GDBusProxy* proxy); + + static void OnAccessResponse(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data); + static void OnResponseSignalEmitted(GDBusConnection* connection, + const char* sender_name, + const char* object_path, + const char* interface_name, + const char* signal_name, + GVariant* parameters, + gpointer user_data); + static void OnOpenResponse(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data); + + CameraPortal::PortalNotifier* notifier_ = nullptr; + + GDBusConnection* connection_ = nullptr; + GDBusProxy* proxy_ = nullptr; + GCancellable* cancellable_ = nullptr; + guint access_request_signal_id_ = 0; +}; + +CameraPortalPrivate::CameraPortalPrivate(CameraPortal::PortalNotifier* notifier) + : notifier_(notifier) {} + +CameraPortalPrivate::~CameraPortalPrivate() { + if (access_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(connection_, + access_request_signal_id_); + access_request_signal_id_ = 0; + } + if (cancellable_) { + g_cancellable_cancel(cancellable_); + g_object_unref(cancellable_); + cancellable_ = nullptr; + } + if (proxy_) { + g_object_unref(proxy_); + proxy_ = nullptr; + connection_ = nullptr; + } +} + +void CameraPortalPrivate::Start() { + cancellable_ = g_cancellable_new(); + Scoped<GError> error; + RequestSessionProxy(kCameraInterfaceName, OnProxyRequested, cancellable_, + this); +} + +// static +void CameraPortalPrivate::OnProxyRequested(GObject* gobject, + GAsyncResult* result, + gpointer user_data) { + CameraPortalPrivate* that = static_cast<CameraPortalPrivate*>(user_data); + Scoped<GError> error; + GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive()); + if (!proxy) { + // Ignore the error caused by user cancelling the request via `cancellable_` + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + RTC_LOG(LS_ERROR) << "Failed to get a proxy for the portal: " + << error->message; + that->OnPortalDone(RequestResponse::kError); + return; + } + + RTC_LOG(LS_VERBOSE) << "Successfully created proxy for the portal."; + that->ProxyRequested(proxy); +} + +void CameraPortalPrivate::ProxyRequested(GDBusProxy* proxy) { + GVariantBuilder builder; + Scoped<char> variant_string; + std::string access_handle; + + proxy_ = proxy; + connection_ = g_dbus_proxy_get_connection(proxy); + + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + variant_string = + g_strdup_printf("capture%d", g_random_int_range(0, G_MAXINT)); + g_variant_builder_add(&builder, "{sv}", "handle_token", + g_variant_new_string(variant_string.get())); + + access_handle = + xdg_portal::PrepareSignalHandle(variant_string.get(), connection_); + access_request_signal_id_ = xdg_portal::SetupRequestResponseSignal( + access_handle.c_str(), OnResponseSignalEmitted, this, connection_); + + RTC_LOG(LS_VERBOSE) << "Requesting camera access from the portal."; + g_dbus_proxy_call(proxy_, "AccessCamera", g_variant_new("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout_msec=*/-1, cancellable_, + reinterpret_cast<GAsyncReadyCallback>(OnAccessResponse), + this); +} + +// static +void CameraPortalPrivate::OnAccessResponse(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data) { + CameraPortalPrivate* that = static_cast<CameraPortalPrivate*>(user_data); + RTC_DCHECK(that); + + Scoped<GError> error; + Scoped<GVariant> variant( + g_dbus_proxy_call_finish(proxy, result, error.receive())); + if (!variant) { + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + RTC_LOG(LS_ERROR) << "Failed to access portal:" << error->message; + if (that->access_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(that->connection_, + that->access_request_signal_id_); + that->access_request_signal_id_ = 0; + } + that->OnPortalDone(RequestResponse::kError); + } +} + +// static +void CameraPortalPrivate::OnResponseSignalEmitted(GDBusConnection* connection, + const char* sender_name, + const char* object_path, + const char* interface_name, + const char* signal_name, + GVariant* parameters, + gpointer user_data) { + CameraPortalPrivate* that = static_cast<CameraPortalPrivate*>(user_data); + RTC_DCHECK(that); + + uint32_t portal_response; + g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr); + if (portal_response) { + RTC_LOG(LS_INFO) << "Camera access denied by the XDG portal."; + that->OnPortalDone(RequestResponseFromPortalResponse(portal_response)); + return; + } + + RTC_LOG(LS_VERBOSE) << "Camera access granted by the XDG portal."; + + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); + + g_dbus_proxy_call( + that->proxy_, "OpenPipeWireRemote", g_variant_new("(a{sv})", &builder), + G_DBUS_CALL_FLAGS_NONE, /*timeout_msec=*/-1, that->cancellable_, + reinterpret_cast<GAsyncReadyCallback>(OnOpenResponse), that); +} + +void CameraPortalPrivate::OnOpenResponse(GDBusProxy* proxy, + GAsyncResult* result, + gpointer user_data) { + CameraPortalPrivate* that = static_cast<CameraPortalPrivate*>(user_data); + RTC_DCHECK(that); + + Scoped<GError> error; + Scoped<GUnixFDList> outlist; + Scoped<GVariant> variant(g_dbus_proxy_call_with_unix_fd_list_finish( + proxy, outlist.receive(), result, error.receive())); + if (!variant) { + if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + RTC_LOG(LS_ERROR) << "Failed to open PipeWire remote:" << error->message; + if (that->access_request_signal_id_) { + g_dbus_connection_signal_unsubscribe(that->connection_, + that->access_request_signal_id_); + that->access_request_signal_id_ = 0; + } + that->OnPortalDone(RequestResponse::kError); + return; + } + + int32_t index; + g_variant_get(variant.get(), "(h)", &index); + + int fd = g_unix_fd_list_get(outlist.get(), index, error.receive()); + + if (fd == kInvalidPipeWireFd) { + RTC_LOG(LS_ERROR) << "Failed to get file descriptor from the list: " + << error->message; + that->OnPortalDone(RequestResponse::kError); + return; + } + + that->OnPortalDone(RequestResponse::kSuccess, fd); +} + +void CameraPortalPrivate::OnPortalDone(RequestResponse result, int fd) { + notifier_->OnCameraRequestResult(result, fd); +} + +CameraPortal::CameraPortal(PortalNotifier* notifier) + : private_(std::make_unique<CameraPortalPrivate>(notifier)) {} + +CameraPortal::~CameraPortal() {} + +void CameraPortal::Start() { + private_->Start(); +} + +} // namespace webrtc |