
The portal camera API in combination with pipewire provides a high-level API for camera access. It makes it possible to grant access to the camera in sandboxed environments. For compatibility a fallback to the v4l2 backend is implemented in case no portal provider is found at runtime. Bug: webrtc:13177 Change-Id: I827017e85b430f01e0832d551cac20f8c38e026f Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3308882 Reviewed-by: Mirko Bonadei <mbonadei@chromium.org> Reviewed-by: Tomas Gunnarsson <tommi@chromium.org> Reviewed-by: Ilya Nikolaevskiy <ilnik@chromium.org> Reviewed-by: Mark Foltz <mfoltz@chromium.org> Reviewed-by: Johannes Kron <kron@chromium.org> Reviewed-by: Thomas Anderson <thomasanderson@chromium.org> Commit-Queue: Thomas Anderson <thomasanderson@chromium.org> Reviewed-by: Nico Weber <thakis@chromium.org> Cr-Commit-Position: refs/heads/main@{#1306850}
194 lines
7.3 KiB
C++
194 lines
7.3 KiB
C++
// Copyright 2019 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "content/browser/video_capture_service_impl.h"
|
|
|
|
#include "base/no_destructor.h"
|
|
#include "base/task/thread_pool.h"
|
|
#include "base/threading/sequence_local_storage_slot.h"
|
|
#include "base/time/time.h"
|
|
#include "build/build_config.h"
|
|
#include "content/public/browser/browser_task_traits.h"
|
|
#include "content/public/browser/browser_thread.h"
|
|
#include "content/public/browser/child_process_host.h"
|
|
#include "content/public/browser/service_process_host.h"
|
|
#include "content/public/browser/service_process_host_passkeys.h"
|
|
#include "content/public/browser/video_capture_service.h"
|
|
#include "content/public/common/content_features.h"
|
|
#include "content/public/common/content_switches.h"
|
|
#include "mojo/public/cpp/bindings/receiver_set.h"
|
|
#include "mojo/public/cpp/bindings/remote.h"
|
|
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
|
|
#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
|
|
#include "services/video_capture/video_capture_service_impl.h"
|
|
|
|
#if BUILDFLAG(IS_WIN)
|
|
#define CREATE_IN_PROCESS_TASK_RUNNER base::ThreadPool::CreateCOMSTATaskRunner
|
|
#else
|
|
#define CREATE_IN_PROCESS_TASK_RUNNER \
|
|
base::ThreadPool::CreateSingleThreadTaskRunner
|
|
#endif
|
|
|
|
namespace content {
|
|
|
|
namespace {
|
|
|
|
std::atomic<bool> g_use_safe_mode(false);
|
|
|
|
} // namespace
|
|
|
|
// Helper class to allow access to class-based passkeys.
|
|
class VideoCaptureServiceLauncher {
|
|
public:
|
|
static void Launch(
|
|
mojo::PendingReceiver<video_capture::mojom::VideoCaptureService>
|
|
receiver) {
|
|
ServiceProcessHost::Options options;
|
|
options.WithDisplayName("Video Capture");
|
|
// TODO(crbug.com/328099369) Remove once gpu client is provided directly.
|
|
options.WithGpuClient(ServiceProcessHostGpuClient::GetPassKey());
|
|
#if BUILDFLAG(IS_MAC)
|
|
// On Mac, the service requires a CFRunLoop which is provided by a
|
|
// UI message loop. See https://crbug.com/834581.
|
|
options.WithExtraCommandLineSwitches({switches::kMessageLoopTypeUi});
|
|
if (g_use_safe_mode) {
|
|
// When safe-mode is enabled, we keep the original entitlements and the
|
|
// hardened runtime to only load safe DAL plugins and reduce crash risk
|
|
// from third-party DAL plugins.
|
|
// As this is not possible to do with unsigned developer builds, we use
|
|
// an undocumented environment variable that macOS CMIO module checks to
|
|
// prevent loading any plugins.
|
|
setenv("CMIO_DAL_Ignore_Standard_PlugIns", "", 1);
|
|
} else {
|
|
// On Mac, the service also needs to have a different set of
|
|
// entitlements, the reason being that some virtual cameras DAL plugins
|
|
// are not signed or are signed by a different Team ID. Hence,
|
|
// library validation has to be disabled (see
|
|
// http://crbug.com/990381#c21).
|
|
options.WithChildFlags(ChildProcessHost::CHILD_PLUGIN);
|
|
}
|
|
#endif
|
|
#if defined(WEBRTC_USE_PIPEWIRE)
|
|
// The PipeWire camera implementation in webrtc uses gdbus for portal
|
|
// handling, so the glib message loop must be used.
|
|
options.WithExtraCommandLineSwitches({switches::kMessageLoopTypeUi});
|
|
#endif
|
|
|
|
ServiceProcessHost::Launch(std::move(receiver), options.Pass());
|
|
}
|
|
};
|
|
|
|
namespace {
|
|
|
|
video_capture::mojom::VideoCaptureService* g_service_override = nullptr;
|
|
|
|
void BindInProcessInstance(
|
|
mojo::PendingReceiver<video_capture::mojom::VideoCaptureService> receiver) {
|
|
static base::NoDestructor<video_capture::VideoCaptureServiceImpl> service(
|
|
std::move(receiver), GetUIThreadTaskRunner({}),
|
|
/*create_system_monitor=*/false);
|
|
}
|
|
|
|
mojo::Remote<video_capture::mojom::VideoCaptureService>& GetUIThreadRemote() {
|
|
// NOTE: This use of sequence-local storage is only to ensure that the Remote
|
|
// only lives as long as the UI-thread sequence, since the UI-thread sequence
|
|
// may be torn down and reinitialized e.g. between unit tests.
|
|
static base::SequenceLocalStorageSlot<
|
|
mojo::Remote<video_capture::mojom::VideoCaptureService>>
|
|
remote_slot;
|
|
return remote_slot.GetOrCreateValue();
|
|
}
|
|
|
|
// This is a custom traits type we use in conjunction with mojo::ReceiverSetBase
|
|
// so that all dispatched messages can be forwarded to the currently bound UI
|
|
// thread Remote.
|
|
struct ForwardingImplRefTraits {
|
|
using PointerType = void*;
|
|
static bool IsNull(PointerType) { return false; }
|
|
static video_capture::mojom::VideoCaptureService* GetRawPointer(PointerType) {
|
|
return &GetVideoCaptureService();
|
|
}
|
|
};
|
|
|
|
// If |GetVideoCaptureService()| is called from off the UI thread, return a
|
|
// sequence-local Remote. Its corresponding receiver will be bound in this set,
|
|
// forwarding to the current UI-thread Remote.
|
|
void BindProxyRemoteOnUIThread(
|
|
mojo::PendingReceiver<video_capture::mojom::VideoCaptureService> receiver) {
|
|
static base::NoDestructor<mojo::ReceiverSetBase<
|
|
mojo::Receiver<video_capture::mojom::VideoCaptureService,
|
|
ForwardingImplRefTraits>,
|
|
void>>
|
|
receivers;
|
|
receivers->Add(nullptr, std::move(receiver));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
void EnableVideoCaptureServiceSafeMode() {
|
|
LOG(WARNING) << "Enabling safe mode VideoCaptureService";
|
|
g_use_safe_mode = true;
|
|
}
|
|
|
|
video_capture::mojom::VideoCaptureService& GetVideoCaptureService() {
|
|
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
|
|
static base::SequenceLocalStorageSlot<
|
|
mojo::Remote<video_capture::mojom::VideoCaptureService>>
|
|
storage;
|
|
auto& remote = storage.GetOrCreateValue();
|
|
if (!remote.is_bound()) {
|
|
GetUIThreadTaskRunner({})->PostTask(
|
|
FROM_HERE, base::BindOnce(&BindProxyRemoteOnUIThread,
|
|
remote.BindNewPipeAndPassReceiver()));
|
|
}
|
|
return *remote.get();
|
|
}
|
|
|
|
if (g_service_override) {
|
|
return *g_service_override;
|
|
}
|
|
|
|
auto& remote = GetUIThreadRemote();
|
|
if (!remote.is_bound()) {
|
|
auto receiver = remote.BindNewPipeAndPassReceiver();
|
|
if (features::IsVideoCaptureServiceEnabledForBrowserProcess()) {
|
|
auto dedicated_task_runner = CREATE_IN_PROCESS_TASK_RUNNER(
|
|
{base::MayBlock(), base::WithBaseSyncPrimitives(),
|
|
base::TaskPriority::BEST_EFFORT},
|
|
base::SingleThreadTaskRunnerThreadMode::DEDICATED);
|
|
dedicated_task_runner->PostTask(
|
|
FROM_HERE,
|
|
base::BindOnce(&BindInProcessInstance, std::move(receiver)));
|
|
} else {
|
|
// Launch in a utility service.
|
|
VideoCaptureServiceLauncher::Launch(std::move(receiver));
|
|
#if !BUILDFLAG(IS_ANDROID)
|
|
// On Android, we do not use automatic service shutdown, because when
|
|
// shutting down the service, we lose caching of the supported formats,
|
|
// and re-querying these can take several seconds on certain Android
|
|
// devices.
|
|
remote.set_idle_handler(
|
|
base::Seconds(5),
|
|
base::BindRepeating(
|
|
[](mojo::Remote<video_capture::mojom::VideoCaptureService>*
|
|
remote) { remote->reset(); },
|
|
&remote));
|
|
#endif // !BUILDFLAG(IS_ANDROID)
|
|
|
|
// Make sure the Remote is also reset in case of e.g. service crash so we
|
|
// can restart it as needed.
|
|
remote.reset_on_disconnect();
|
|
}
|
|
}
|
|
|
|
return *remote.get();
|
|
}
|
|
|
|
void OverrideVideoCaptureServiceForTesting( // IN-TEST
|
|
video_capture::mojom::VideoCaptureService* service) {
|
|
g_service_override = service;
|
|
}
|
|
|
|
} // namespace content
|