blob: 8d14685d7cd8d0329162155770d3f922732e1d38 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2019 The Chromium Authors
Ken Rockot2f8c1dd22019-07-23 21:56:022// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Florent Castelli8764dd452023-02-09 13:43:305#include "content/browser/video_capture_service_impl.h"
Ken Rockot2f8c1dd22019-07-23 21:56:026
7#include "base/no_destructor.h"
Gabriel Charette83d937e2020-02-29 01:17:568#include "base/task/thread_pool.h"
Ken Rockot2f8c1dd22019-07-23 21:56:029#include "base/threading/sequence_local_storage_slot.h"
10#include "base/time/time.h"
11#include "build/build_config.h"
12#include "content/public/browser/browser_task_traits.h"
13#include "content/public/browser/browser_thread.h"
Will Harriscd57b832023-01-05 20:03:1014#include "content/public/browser/child_process_host.h"
Ken Rockot2f8c1dd22019-07-23 21:56:0215#include "content/public/browser/service_process_host.h"
Alex Gough6a170e6d2024-03-07 00:07:2216#include "content/public/browser/service_process_host_passkeys.h"
Florent Castelli8764dd452023-02-09 13:43:3017#include "content/public/browser/video_capture_service.h"
Ken Rockot2f8c1dd22019-07-23 21:56:0218#include "content/public/common/content_features.h"
19#include "content/public/common/content_switches.h"
20#include "mojo/public/cpp/bindings/receiver_set.h"
21#include "mojo/public/cpp/bindings/remote.h"
22#include "mojo/public/cpp/bindings/self_owned_receiver.h"
Alex Gough0177d792021-08-12 19:39:4923#include "services/video_capture/public/mojom/video_capture_service.mojom.h"
Ken Rockot2f8c1dd22019-07-23 21:56:0224#include "services/video_capture/video_capture_service_impl.h"
25
Xiaohan Wang1ecfd002022-01-19 22:33:1026#if BUILDFLAG(IS_WIN)
Gabriel Charette83d937e2020-02-29 01:17:5627#define CREATE_IN_PROCESS_TASK_RUNNER base::ThreadPool::CreateCOMSTATaskRunner
Ken Rockot2f8c1dd22019-07-23 21:56:0228#else
Gabriel Charette83d937e2020-02-29 01:17:5629#define CREATE_IN_PROCESS_TASK_RUNNER \
30 base::ThreadPool::CreateSingleThreadTaskRunner
Ken Rockot2f8c1dd22019-07-23 21:56:0231#endif
32
33namespace content {
34
35namespace {
Alex Gough6a170e6d2024-03-07 00:07:2236
Florent Castelli8764dd452023-02-09 13:43:3037std::atomic<bool> g_use_safe_mode(false);
Ken Rockot2f8c1dd22019-07-23 21:56:0238
Alex Gough6a170e6d2024-03-07 00:07:2239} // namespace
40
41// Helper class to allow access to class-based passkeys.
42class VideoCaptureServiceLauncher {
43 public:
44 static void Launch(
45 mojo::PendingReceiver<video_capture::mojom::VideoCaptureService>
46 receiver) {
47 ServiceProcessHost::Options options;
48 options.WithDisplayName("Video Capture");
49 // TODO(crbug.com/328099369) Remove once gpu client is provided directly.
50 options.WithGpuClient(ServiceProcessHostGpuClient::GetPassKey());
51#if BUILDFLAG(IS_MAC)
52 // On Mac, the service requires a CFRunLoop which is provided by a
53 // UI message loop. See https://crbug.com/834581.
54 options.WithExtraCommandLineSwitches({switches::kMessageLoopTypeUi});
55 if (g_use_safe_mode) {
56 // When safe-mode is enabled, we keep the original entitlements and the
57 // hardened runtime to only load safe DAL plugins and reduce crash risk
58 // from third-party DAL plugins.
59 // As this is not possible to do with unsigned developer builds, we use
60 // an undocumented environment variable that macOS CMIO module checks to
61 // prevent loading any plugins.
62 setenv("CMIO_DAL_Ignore_Standard_PlugIns", "", 1);
63 } else {
64 // On Mac, the service also needs to have a different set of
65 // entitlements, the reason being that some virtual cameras DAL plugins
66 // are not signed or are signed by a different Team ID. Hence,
67 // library validation has to be disabled (see
68 // http://crbug.com/990381#c21).
69 options.WithChildFlags(ChildProcessHost::CHILD_PLUGIN);
70 }
71#endif
Michael Olbrichedcd01332024-05-28 17:23:0472#if defined(WEBRTC_USE_PIPEWIRE)
73 // The PipeWire camera implementation in webrtc uses gdbus for portal
74 // handling, so the glib message loop must be used.
75 options.WithExtraCommandLineSwitches({switches::kMessageLoopTypeUi});
76#endif
Alex Gough6a170e6d2024-03-07 00:07:2277
78 ServiceProcessHost::Launch(std::move(receiver), options.Pass());
79 }
80};
81
82namespace {
83
Ken Rockot2f8c1dd22019-07-23 21:56:0284video_capture::mojom::VideoCaptureService* g_service_override = nullptr;
85
86void BindInProcessInstance(
87 mojo::PendingReceiver<video_capture::mojom::VideoCaptureService> receiver) {
88 static base::NoDestructor<video_capture::VideoCaptureServiceImpl> service(
Palak Agarwalf0416e2b2023-11-24 21:33:5489 std::move(receiver), GetUIThreadTaskRunner({}),
90 /*create_system_monitor=*/false);
Ken Rockot2f8c1dd22019-07-23 21:56:0291}
92
93mojo::Remote<video_capture::mojom::VideoCaptureService>& GetUIThreadRemote() {
Ken Rockot72964402019-12-06 10:40:5194 // NOTE: This use of sequence-local storage is only to ensure that the Remote
95 // only lives as long as the UI-thread sequence, since the UI-thread sequence
96 // may be torn down and reinitialized e.g. between unit tests.
Avi Drissmanded77172021-07-02 18:23:0097 static base::SequenceLocalStorageSlot<
98 mojo::Remote<video_capture::mojom::VideoCaptureService>>
Ken Rockot72964402019-12-06 10:40:5199 remote_slot;
Avi Drissmanded77172021-07-02 18:23:00100 return remote_slot.GetOrCreateValue();
Ken Rockot2f8c1dd22019-07-23 21:56:02101}
102
103// This is a custom traits type we use in conjunction with mojo::ReceiverSetBase
104// so that all dispatched messages can be forwarded to the currently bound UI
105// thread Remote.
106struct ForwardingImplRefTraits {
107 using PointerType = void*;
108 static bool IsNull(PointerType) { return false; }
109 static video_capture::mojom::VideoCaptureService* GetRawPointer(PointerType) {
110 return &GetVideoCaptureService();
111 }
112};
113
114// If |GetVideoCaptureService()| is called from off the UI thread, return a
115// sequence-local Remote. Its corresponding receiver will be bound in this set,
116// forwarding to the current UI-thread Remote.
117void BindProxyRemoteOnUIThread(
118 mojo::PendingReceiver<video_capture::mojom::VideoCaptureService> receiver) {
119 static base::NoDestructor<mojo::ReceiverSetBase<
120 mojo::Receiver<video_capture::mojom::VideoCaptureService,
121 ForwardingImplRefTraits>,
122 void>>
123 receivers;
124 receivers->Add(nullptr, std::move(receiver));
125}
126
127} // namespace
128
Florent Castelli8764dd452023-02-09 13:43:30129void EnableVideoCaptureServiceSafeMode() {
130 LOG(WARNING) << "Enabling safe mode VideoCaptureService";
131 g_use_safe_mode = true;
132}
133
Ken Rockot2f8c1dd22019-07-23 21:56:02134video_capture::mojom::VideoCaptureService& GetVideoCaptureService() {
135 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
Avi Drissmanded77172021-07-02 18:23:00136 static base::SequenceLocalStorageSlot<
137 mojo::Remote<video_capture::mojom::VideoCaptureService>>
Ken Rockot2f8c1dd22019-07-23 21:56:02138 storage;
Avi Drissmanded77172021-07-02 18:23:00139 auto& remote = storage.GetOrCreateValue();
Ken Rockot2f8c1dd22019-07-23 21:56:02140 if (!remote.is_bound()) {
Gabriel Charettee7cdc5cd2020-05-27 23:35:05141 GetUIThreadTaskRunner({})->PostTask(
142 FROM_HERE, base::BindOnce(&BindProxyRemoteOnUIThread,
Ken Rockot2f8c1dd22019-07-23 21:56:02143 remote.BindNewPipeAndPassReceiver()));
144 }
145 return *remote.get();
146 }
147
Florent Castelli8764dd452023-02-09 13:43:30148 if (g_service_override) {
Ken Rockot2f8c1dd22019-07-23 21:56:02149 return *g_service_override;
Florent Castelli8764dd452023-02-09 13:43:30150 }
Ken Rockot2f8c1dd22019-07-23 21:56:02151
152 auto& remote = GetUIThreadRemote();
153 if (!remote.is_bound()) {
154 auto receiver = remote.BindNewPipeAndPassReceiver();
155 if (features::IsVideoCaptureServiceEnabledForBrowserProcess()) {
156 auto dedicated_task_runner = CREATE_IN_PROCESS_TASK_RUNNER(
Gabriel Charette83d937e2020-02-29 01:17:56157 {base::MayBlock(), base::WithBaseSyncPrimitives(),
158 base::TaskPriority::BEST_EFFORT},
Ken Rockot2f8c1dd22019-07-23 21:56:02159 base::SingleThreadTaskRunnerThreadMode::DEDICATED);
160 dedicated_task_runner->PostTask(
161 FROM_HERE,
162 base::BindOnce(&BindInProcessInstance, std::move(receiver)));
163 } else {
Alex Gough6a170e6d2024-03-07 00:07:22164 // Launch in a utility service.
165 VideoCaptureServiceLauncher::Launch(std::move(receiver));
Xiaohan Wang1ecfd002022-01-19 22:33:10166#if !BUILDFLAG(IS_ANDROID)
Ken Rockot2f8c1dd22019-07-23 21:56:02167 // On Android, we do not use automatic service shutdown, because when
168 // shutting down the service, we lose caching of the supported formats,
169 // and re-querying these can take several seconds on certain Android
170 // devices.
171 remote.set_idle_handler(
Peter Kastinge5a38ed2021-10-02 03:06:35172 base::Seconds(5),
Ken Rockot2f8c1dd22019-07-23 21:56:02173 base::BindRepeating(
174 [](mojo::Remote<video_capture::mojom::VideoCaptureService>*
Ilya Nikolaevskiy3cd4d182022-11-29 13:49:03175 remote) { remote->reset(); },
Ken Rockot2f8c1dd22019-07-23 21:56:02176 &remote));
Xiaohan Wang1ecfd002022-01-19 22:33:10177#endif // !BUILDFLAG(IS_ANDROID)
Ken Rockot2f8c1dd22019-07-23 21:56:02178
179 // Make sure the Remote is also reset in case of e.g. service crash so we
180 // can restart it as needed.
181 remote.reset_on_disconnect();
182 }
183 }
184
185 return *remote.get();
186}
187
Florent Castelli8764dd452023-02-09 13:43:30188void OverrideVideoCaptureServiceForTesting( // IN-TEST
Ken Rockot2f8c1dd22019-07-23 21:56:02189 video_capture::mojom::VideoCaptureService* service) {
190 g_service_override = service;
191}
192
193} // namespace content