mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00

We use value -1 on over all the places through our code so it might be better to define a constant and use it instead to make the code more understandable on first look. Bug: webrtc:15203 Change-Id: I4fc3e561bc7a7778c43ec6cfde7acebef2af79e8 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/306620 Reviewed-by: Alexander Cooper <alcooper@chromium.org> Commit-Queue: Jan Grulich <grulja@gmail.com> Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org> Cr-Commit-Position: refs/heads/main@{#40156}
244 lines
8.6 KiB
C++
244 lines
8.6 KiB
C++
/*
|
|
* 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
|