summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/libwebrtc/modules/video_capture/linux/camera_portal.cc
parentInitial commit. (diff)
downloadfirefox-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.cc244
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