blob: 94ea844c61d11a829ecbf39211ab933fe3cc8008 [file] [log] [blame]
// 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/post_task.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/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::CreateCOMSTATaskRunner
#else
#define CREATE_IN_PROCESS_TASK_RUNNER base::CreateSingleThreadTaskRunner
#endif
namespace content {
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),
base::CreateSingleThreadTaskRunner({BrowserThread::UI}));
}
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::NoDestructor<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
video_capture::mojom::VideoCaptureService& GetVideoCaptureService() {
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
static base::NoDestructor<base::SequenceLocalStorageSlot<
mojo::Remote<video_capture::mojom::VideoCaptureService>>>
storage;
auto& remote = storage->GetOrCreateValue();
if (!remote.is_bound()) {
base::CreateSingleThreadTaskRunner({BrowserThread::UI})
->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::TaskTraits{base::ThreadPool(), 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")
.WithSandboxType(service_manager::SandboxType::kNoSandbox)
#if defined(OS_MACOSX)
// 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<video_capture::mojom::VideoCaptureService>*
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