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
content / browser / video_capture_service_impl.cc [blame]
// 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