1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
// 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
|