| // Copyright 2017 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/browser/renderer_host/media/service_video_capture_provider.h" |
| |
| #include "content/browser/renderer_host/media/service_video_capture_device_launcher.h" |
| #include "content/browser/renderer_host/media/video_capture_factory_delegate.h" |
| #include "content/common/child_process_host_impl.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/delegate_to_browser_gpu_service_accelerator_factory.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "mojo/public/cpp/bindings/callback_helpers.h" |
| #include "mojo/public/cpp/bindings/strong_binding.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "services/video_capture/public/mojom/constants.mojom.h" |
| #include "services/video_capture/public/uma/video_capture_service_event.h" |
| |
| namespace { |
| |
| class ServiceConnectorImpl |
| : public content::ServiceVideoCaptureProvider::ServiceConnector { |
| public: |
| ServiceConnectorImpl() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| // In unit test environments, there may not be any connector. |
| auto* connection = content::ServiceManagerConnection::GetForProcess(); |
| if (!connection) |
| return; |
| auto* connector = connection->GetConnector(); |
| if (!connector) |
| return; |
| connector_ = connector->Clone(); |
| } |
| |
| void BindFactoryProvider( |
| video_capture::mojom::DeviceFactoryProviderPtr* provider) override { |
| if (!connector_) { |
| CHECK(false) << "Attempted to connect to the video capture service from " |
| "a process that does not provide a " |
| "ServiceManagerConnection"; |
| } |
| connector_->BindInterface(video_capture::mojom::kServiceName, provider); |
| } |
| |
| private: |
| std::unique_ptr<service_manager::Connector> connector_; |
| }; |
| |
| std::unique_ptr<video_capture::mojom::AcceleratorFactory> |
| CreateAcceleratorFactory() { |
| return std::make_unique< |
| content::DelegateToBrowserGpuServiceAcceleratorFactory>(); |
| } |
| |
| } // anonymous namespace |
| |
| namespace content { |
| |
| ServiceVideoCaptureProvider::ServiceVideoCaptureProvider( |
| base::RepeatingCallback<void(const std::string&)> emit_log_message_cb) |
| : ServiceVideoCaptureProvider( |
| std::make_unique<ServiceConnectorImpl>(), |
| base::BindRepeating(&CreateAcceleratorFactory), |
| std::move(emit_log_message_cb)) {} |
| |
| ServiceVideoCaptureProvider::ServiceVideoCaptureProvider( |
| std::unique_ptr<ServiceConnector> service_connector, |
| CreateAcceleratorFactoryCallback create_accelerator_factory_cb, |
| base::RepeatingCallback<void(const std::string&)> emit_log_message_cb) |
| : service_connector_(std::move(service_connector)), |
| create_accelerator_factory_cb_(std::move(create_accelerator_factory_cb)), |
| emit_log_message_cb_(std::move(emit_log_message_cb)), |
| usage_count_(0), |
| launcher_has_connected_to_device_factory_(false), |
| weak_ptr_factory_(this) {} |
| |
| ServiceVideoCaptureProvider::~ServiceVideoCaptureProvider() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| UninitializeInternal(ReasonForUninitialize::kShutdown); |
| } |
| |
| void ServiceVideoCaptureProvider::GetDeviceInfosAsync( |
| GetDeviceInfosCallback result_callback) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| emit_log_message_cb_.Run("ServiceVideoCaptureProvider::GetDeviceInfosAsync"); |
| IncreaseUsageCount(); |
| LazyConnectToService(); |
| // Use a ScopedCallbackRunner to make sure that |result_callback| gets |
| // invoked with an empty result in case that the service drops the request. |
| device_factory_->GetDeviceInfos(mojo::WrapCallbackWithDefaultInvokeIfNotRun( |
| base::BindOnce(&ServiceVideoCaptureProvider::OnDeviceInfosReceived, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(result_callback)), |
| std::vector<media::VideoCaptureDeviceInfo>())); |
| } |
| |
| std::unique_ptr<VideoCaptureDeviceLauncher> |
| ServiceVideoCaptureProvider::CreateDeviceLauncher() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| return std::make_unique<ServiceVideoCaptureDeviceLauncher>( |
| base::BindRepeating(&ServiceVideoCaptureProvider::ConnectToDeviceFactory, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void ServiceVideoCaptureProvider::ConnectToDeviceFactory( |
| std::unique_ptr<VideoCaptureFactoryDelegate>* out_factory) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| IncreaseUsageCount(); |
| LazyConnectToService(); |
| launcher_has_connected_to_device_factory_ = true; |
| *out_factory = std::make_unique<VideoCaptureFactoryDelegate>( |
| &device_factory_, |
| base::BindOnce(&ServiceVideoCaptureProvider::DecreaseUsageCount, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void ServiceVideoCaptureProvider::LazyConnectToService() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| if (device_factory_provider_.is_bound()) |
| return; |
| |
| video_capture::uma::LogVideoCaptureServiceEvent( |
| video_capture::uma::BROWSER_CONNECTING_TO_SERVICE); |
| if (time_of_last_uninitialize_ != base::TimeTicks()) { |
| if (launcher_has_connected_to_device_factory_) { |
| video_capture::uma::LogDurationUntilReconnectAfterCapture( |
| base::TimeTicks::Now() - time_of_last_uninitialize_); |
| } else { |
| video_capture::uma::LogDurationUntilReconnectAfterEnumerationOnly( |
| base::TimeTicks::Now() - time_of_last_uninitialize_); |
| } |
| } |
| |
| launcher_has_connected_to_device_factory_ = false; |
| time_of_last_connect_ = base::TimeTicks::Now(); |
| |
| video_capture::mojom::AcceleratorFactoryPtr accelerator_factory; |
| mojo::MakeStrongBinding(create_accelerator_factory_cb_.Run(), |
| mojo::MakeRequest(&accelerator_factory)); |
| service_connector_->BindFactoryProvider(&device_factory_provider_); |
| device_factory_provider_->InjectGpuDependencies( |
| std::move(accelerator_factory)); |
| device_factory_provider_->ConnectToDeviceFactory( |
| mojo::MakeRequest(&device_factory_)); |
| // Unretained |this| is safe, because |this| owns |device_factory_|. |
| device_factory_.set_connection_error_handler(base::BindOnce( |
| &ServiceVideoCaptureProvider::OnLostConnectionToDeviceFactory, |
| base::Unretained(this))); |
| } |
| |
| void ServiceVideoCaptureProvider::OnDeviceInfosReceived( |
| GetDeviceInfosCallback result_callback, |
| const std::vector<media::VideoCaptureDeviceInfo>& infos) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| base::ResetAndReturn(&result_callback).Run(infos); |
| DecreaseUsageCount(); |
| } |
| |
| void ServiceVideoCaptureProvider::OnLostConnectionToDeviceFactory() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| emit_log_message_cb_.Run( |
| "ServiceVideoCaptureProvider::OnLostConnectionToDeviceFactory"); |
| // This may indicate that the video capture service has crashed. Uninitialize |
| // here, so that a new connection will be established when clients try to |
| // reconnect. |
| UninitializeInternal(ReasonForUninitialize::kConnectionLost); |
| } |
| |
| void ServiceVideoCaptureProvider::IncreaseUsageCount() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| usage_count_++; |
| } |
| |
| void ServiceVideoCaptureProvider::DecreaseUsageCount() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| usage_count_--; |
| DCHECK_GE(usage_count_, 0); |
| if (usage_count_ == 0) |
| UninitializeInternal(ReasonForUninitialize::kUnused); |
| } |
| |
| void ServiceVideoCaptureProvider::UninitializeInternal( |
| ReasonForUninitialize reason) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
| if (!device_factory_.is_bound()) { |
| return; |
| } |
| base::TimeDelta duration_since_last_connect(base::TimeTicks::Now() - |
| time_of_last_connect_); |
| switch (reason) { |
| case ReasonForUninitialize::kShutdown: |
| case ReasonForUninitialize::kUnused: |
| if (launcher_has_connected_to_device_factory_) { |
| video_capture::uma::LogVideoCaptureServiceEvent( |
| video_capture::uma:: |
| BROWSER_CLOSING_CONNECTION_TO_SERVICE_AFTER_CAPTURE); |
| video_capture::uma:: |
| LogDurationFromLastConnectToClosingConnectionAfterCapture( |
| duration_since_last_connect); |
| } else { |
| video_capture::uma::LogVideoCaptureServiceEvent( |
| video_capture::uma:: |
| BROWSER_CLOSING_CONNECTION_TO_SERVICE_AFTER_ENUMERATION_ONLY); |
| video_capture::uma:: |
| LogDurationFromLastConnectToClosingConnectionAfterEnumerationOnly( |
| duration_since_last_connect); |
| } |
| break; |
| case ReasonForUninitialize::kConnectionLost: |
| video_capture::uma::LogVideoCaptureServiceEvent( |
| video_capture::uma::BROWSER_LOST_CONNECTION_TO_SERVICE); |
| video_capture::uma::LogDurationFromLastConnectToConnectionLost( |
| duration_since_last_connect); |
| break; |
| } |
| device_factory_.reset(); |
| device_factory_provider_.reset(); |
| time_of_last_uninitialize_ = base::TimeTicks::Now(); |
| } |
| |
| } // namespace content |