// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/public/browser/video_capture_service.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/browser/service_sandbox_type.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/service_process_host.h" #include "content/public/common/child_process_host.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/uma/video_capture_service_event.h" #include "services/video_capture/video_capture_service_impl.h" #if defined(OS_WIN) #define CREATE_IN_PROCESS_TASK_RUNNER base::ThreadPool::CreateCOMSTATaskRunner #else #define CREATE_IN_PROCESS_TASK_RUNNER \ base::ThreadPool::CreateSingleThreadTaskRunner #endif namespace content { namespace { video_capture::mojom::VideoCaptureService* g_service_override = nullptr; void BindInProcessInstance( mojo::PendingReceiver receiver) { static base::NoDestructor service( std::move(receiver), GetUIThreadTaskRunner({})); } mojo::Remote& 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::NoDestructor>> 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 receiver) { static base::NoDestructor, void>> receivers; receivers->Add(nullptr, std::move(receiver)); } } // namespace video_capture::mojom::VideoCaptureService& GetVideoCaptureService() { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { static base::NoDestructor>> 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 { ServiceProcessHost::Launch( std::move(receiver), ServiceProcessHost::Options() .WithDisplayName("Video Capture") #if defined(OS_MAC) // On Mac, the service requires a CFRunLoop which is provided by a // UI message loop. See https://crbug.com/834581. .WithExtraCommandLineSwitches({switches::kMessageLoopTypeUi}) // On Mac, the service also needs to have a different set of // entitlements, the reason being that some virtual cameras // are not signed or are signed by a different Team ID. Hence, // library validation has to be disabled (see // http://crbug.com/990381#c21). .WithChildFlags(ChildProcessHost::CHILD_PLUGIN) #endif .Pass()); #if !defined(OS_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::TimeDelta::FromSeconds(5), base::BindRepeating( [](mojo::Remote* remote) { video_capture::uma::LogVideoCaptureServiceEvent( video_capture::uma :: SERVICE_SHUTTING_DOWN_BECAUSE_NO_CLIENT); remote->reset(); }, &remote)); #endif // !defined(OS_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( video_capture::mojom::VideoCaptureService* service) { g_service_override = service; } } // namespace content