910e62b5创建于 1月15日历史提交
// Copyright 2017 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/renderer_host/media/service_video_capture_provider.h"

#include <utility>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/metrics/histogram_functions.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "content/browser/child_process_host_impl.h"
#include "content/browser/renderer_host/media/service_video_capture_device_launcher.h"
#include "content/browser/renderer_host/media/virtual_video_capture_devices_changed_observer.h"
#include "content/browser/video_capture_service_impl.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/gpu_data_manager.h"
#include "content/public/browser/video_capture_service.h"
#include "content/public/common/content_features.h"
#include "gpu/config/gpu_info.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/bindings/pending_remote.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"

#if BUILDFLAG(IS_CHROMEOS)
#include "content/public/browser/chromeos/delegate_to_browser_gpu_service_accelerator_factory.h"
#endif  // BUILDFLAG(IS_CHROMEOS)

namespace {

using GetSourceInfosResult =
    video_capture::mojom::VideoSourceProvider::GetSourceInfosResult;

#if BUILDFLAG(IS_CHROMEOS)
std::unique_ptr<video_capture::mojom::AcceleratorFactory>
CreateAcceleratorFactory() {
  return std::make_unique<
      content::DelegateToBrowserGpuServiceAcceleratorFactory>();
}
#endif  // BUILDFLAG(IS_CHROMEOS)

// Do not reorder, used for UMA Media.VideoCapture.GetDeviceInfosResult
enum class GetDeviceInfosResult {
  kSucessNoRetry = 0,
  kFailureNoRetry = 1,
  kSucessAfterRetry = 2,
  kFailureAfterRetry = 3,
  kMaxValue = kFailureAfterRetry,
};

void LogGetDeviceInfosResult(
    std::optional<GetSourceInfosResult> get_source_infos_result,
    bool get_device_infos_retried) {
  GetDeviceInfosResult result;
  if (get_source_infos_result &&
      *get_source_infos_result == GetSourceInfosResult::kSuccess) {
    result = get_device_infos_retried ? GetDeviceInfosResult::kSucessAfterRetry
                                      : GetDeviceInfosResult::kSucessNoRetry;
  } else {
    result = get_device_infos_retried ? GetDeviceInfosResult::kFailureAfterRetry
                                      : GetDeviceInfosResult::kFailureNoRetry;
  }
  base::UmaHistogramEnumeration("Media.VideoCapture.GetDeviceInfosResult",
                                result);
}

}  // anonymous namespace

namespace content {

class ServiceVideoCaptureProvider::ServiceProcessObserver
    : public ServiceProcessHost::Observer {
 public:
  ServiceProcessObserver(base::RepeatingClosure start_callback,
                         base::RepeatingClosure stop_callback)
      : io_task_runner_(GetIOThreadTaskRunner({})),
        start_callback_(std::move(start_callback)),
        stop_callback_(std::move(stop_callback)) {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    ServiceProcessHost::AddObserver(this);
  }

  ServiceProcessObserver(const ServiceProcessObserver&) = delete;
  ServiceProcessObserver& operator=(const ServiceProcessObserver&) = delete;

  ~ServiceProcessObserver() override {
    DCHECK_CURRENTLY_ON(BrowserThread::UI);
    ServiceProcessHost::RemoveObserver(this);
  }

 private:
  // ServiceProcessHost::Observer implementation.
  void OnServiceProcessLaunched(const ServiceProcessInfo& info) override {
    if (info.IsService<video_capture::mojom::VideoCaptureService>())
      io_task_runner_->PostTask(FROM_HERE, base::BindOnce(start_callback_));
  }

  void OnServiceProcessTerminatedNormally(
      const ServiceProcessInfo& info) override {
    if (info.IsService<video_capture::mojom::VideoCaptureService>())
      io_task_runner_->PostTask(FROM_HERE, base::BindOnce(stop_callback_));
  }

  void OnServiceProcessCrashed(const ServiceProcessInfo& info) override {
    if (info.IsService<video_capture::mojom::VideoCaptureService>()) {
      LOG(WARNING) << "Detected crash of video capture service";
      io_task_runner_->PostTask(FROM_HERE, base::BindOnce(stop_callback_));
    }
  }

  const scoped_refptr<base::TaskRunner> io_task_runner_;
  const base::RepeatingClosure start_callback_;
  const base::RepeatingClosure stop_callback_;
};

#if BUILDFLAG(IS_CHROMEOS)
ServiceVideoCaptureProvider::ServiceVideoCaptureProvider(
    base::RepeatingCallback<void(const std::string&)> emit_log_message_cb)
    : ServiceVideoCaptureProvider(base::NullCallback(),
                                  std::move(emit_log_message_cb)) {}

ServiceVideoCaptureProvider::ServiceVideoCaptureProvider(
    CreateAcceleratorFactoryCallback create_accelerator_factory_cb,
    base::RepeatingCallback<void(const std::string&)> emit_log_message_cb)
    : create_accelerator_factory_cb_(std::move(create_accelerator_factory_cb)),
      emit_log_message_cb_(std::move(emit_log_message_cb)),
      launcher_has_connected_to_source_provider_(false) {
#else   // BUILDFLAG(IS_CHROMEOS)
ServiceVideoCaptureProvider::ServiceVideoCaptureProvider(
    base::RepeatingCallback<void(const std::string&)> emit_log_message_cb)
    : emit_log_message_cb_(std::move(emit_log_message_cb)),
      launcher_has_connected_to_source_provider_(false) {
#endif  // BUILDFLAG(IS_CHROMEOS)
  if (features::IsVideoCaptureServiceEnabledForOutOfProcess()) {
    service_process_observer_.emplace(
        GetUIThreadTaskRunner({}),
        base::BindRepeating(&ServiceVideoCaptureProvider::OnServiceStarted,
                            weak_ptr_factory_.GetWeakPtr()),
        base::BindRepeating(&ServiceVideoCaptureProvider::OnServiceStopped,
                            weak_ptr_factory_.GetWeakPtr()));
  } else if (features::IsVideoCaptureServiceEnabledForBrowserProcess()) {
    // Connect immediately and permanently when the service runs in-process.
    GetIOThreadTaskRunner({})->PostTask(
        FROM_HERE,
        base::BindOnce(&ServiceVideoCaptureProvider::OnServiceStarted,
                       weak_ptr_factory_.GetWeakPtr()));
  }

  // Must register at the IO thread so that callbacks would be also
  // triggered on the IO thread.
  GetIOThreadTaskRunner({})->PostTask(
      FROM_HERE,
      base::BindOnce(&ServiceVideoCaptureProvider::RegisterWithGpuDataManager,
                     weak_ptr_factory_.GetWeakPtr()));
}

ServiceVideoCaptureProvider::~ServiceVideoCaptureProvider() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  OnServiceConnectionClosed(ReasonForDisconnect::kShutdown);
  content::GpuDataManager::GetInstance()->RemoveObserver(this);
}

void ServiceVideoCaptureProvider::GetDeviceInfosAsync(
    GetDeviceInfosCallback result_callback) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  emit_log_message_cb_.Run("ServiceVideoCaptureProvider::GetDeviceInfosAsync");
  get_device_infos_retried_ = false;
  get_device_infos_pending_callbacks_.push_back(std::move(result_callback));
  GetDeviceInfosAsyncForRetry();
}

std::unique_ptr<VideoCaptureDeviceLauncher>
ServiceVideoCaptureProvider::CreateDeviceLauncher() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  return std::make_unique<ServiceVideoCaptureDeviceLauncher>(
      base::BindRepeating(
          &ServiceVideoCaptureProvider::OnLauncherConnectingToSourceProvider,
          weak_ptr_factory_.GetWeakPtr()));
}

void ServiceVideoCaptureProvider::OpenNativeScreenCapturePicker(
    DesktopMediaID::Type type,
    base::OnceCallback<void(DesktopMediaID::Id)> created_callback,
    base::OnceCallback<void(webrtc::DesktopCapturer::Source)> picker_callback,
    base::OnceCallback<void()> cancel_callback,
    base::OnceCallback<void()> error_callback) {
  NOTREACHED();
}

void ServiceVideoCaptureProvider::CloseNativeScreenCapturePicker(
    DesktopMediaID device_id) {
  NOTREACHED();
}

void ServiceVideoCaptureProvider::OnServiceStarted() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  // Whenever the video capture service starts, we register a
  // VirtualVideoCaptureDevicesChangedObserver in order to propagate device
  // change events when virtual devices are added to or removed from the
  // service.
  auto service_connection = LazyConnectToService();
  mojo::PendingRemote<video_capture::mojom::DevicesChangedObserver> observer;
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<VirtualVideoCaptureDevicesChangedObserver>(),
      observer.InitWithNewPipeAndPassReceiver());
  service_connection->source_provider()->RegisterVirtualDevicesChangedObserver(
      std::move(observer),
      true /*raise_event_if_virtual_devices_already_present*/);
}

void ServiceVideoCaptureProvider::OnServiceStopped() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  if (!get_device_infos_pending_callbacks_.empty()) {
    // The service stopped during a device info query.
    TRACE_EVENT_INSTANT0(
        TRACE_DISABLED_BY_DEFAULT("video_and_image_capture"),
        "Video capture service has shut down. Retrying GetDeviceInfos.",
        TRACE_EVENT_SCOPE_PROCESS);
    GetDeviceInfosAsyncForRetry();
  }
}

void ServiceVideoCaptureProvider::OnLauncherConnectingToSourceProvider(
    scoped_refptr<RefCountedVideoSourceProvider>* out_provider) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  launcher_has_connected_to_source_provider_ = true;
  *out_provider = LazyConnectToService();
}

void ServiceVideoCaptureProvider::RegisterWithGpuDataManager() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  content::GpuDataManager::GetInstance()->AddObserver(this);
}

scoped_refptr<RefCountedVideoSourceProvider>
ServiceVideoCaptureProvider::LazyConnectToService() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);

  if (weak_service_connection_) {
    // There already is a connection.
    return base::WrapRefCounted(weak_service_connection_.get());
  }

  launcher_has_connected_to_source_provider_ = false;
  time_of_last_connect_ = base::TimeTicks::Now();

  auto ui_task_runner = GetUIThreadTaskRunner({});
#if BUILDFLAG(IS_CHROMEOS)
  mojo::PendingRemote<video_capture::mojom::AcceleratorFactory>
      accelerator_factory;
  if (!create_accelerator_factory_cb_)
    create_accelerator_factory_cb_ =
        base::BindRepeating(&CreateAcceleratorFactory);
  mojo::MakeSelfOwnedReceiver(
      create_accelerator_factory_cb_.Run(),
      accelerator_factory.InitWithNewPipeAndPassReceiver());
  GetVideoCaptureService().InjectGpuDependencies(
      std::move(accelerator_factory));
#endif  // BUILDFLAG(IS_CHROMEOS)

#if BUILDFLAG(IS_WIN)
  // Pass active gpu info.
  GetVideoCaptureService().OnGpuInfoUpdate(
      content::GpuDataManager::GetInstance()->GetGPUInfo().active_gpu().luid);
#endif

  mojo::Remote<video_capture::mojom::VideoSourceProvider> source_provider;
#if BUILDFLAG(IS_MAC)
  if (get_device_infos_retried_) {
    // If the service crashed once during a device info query, enable the
    // safe-mode VideoCaptureService.
    EnableVideoCaptureServiceSafeMode();
  }
#endif
  GetVideoCaptureService().ConnectToVideoSourceProvider(
      source_provider.BindNewPipeAndPassReceiver());
  source_provider.set_disconnect_handler(base::BindOnce(
      &ServiceVideoCaptureProvider::OnLostConnectionToSourceProvider,
      weak_ptr_factory_.GetWeakPtr()));
  auto result = base::MakeRefCounted<RefCountedVideoSourceProvider>(
      std::move(source_provider),
      base::BindOnce(&ServiceVideoCaptureProvider::OnServiceConnectionClosed,
                     weak_ptr_factory_.GetWeakPtr(),
                     ReasonForDisconnect::kUnused));
  weak_service_connection_ = result->GetWeakPtr();
  return result;
}

void ServiceVideoCaptureProvider::GetDeviceInfosAsyncForRetry() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  auto service_connection = LazyConnectToService();
  // Make sure that the callback gets invoked with an empty result in case
  // that the service drops the request.
  service_connection->source_provider()->GetSourceInfos(
      mojo::WrapCallbackWithDropHandler(
          base::BindOnce(&ServiceVideoCaptureProvider::OnDeviceInfosReceived,
                         weak_ptr_factory_.GetWeakPtr(), service_connection),
          base::BindOnce(
              &ServiceVideoCaptureProvider::OnDeviceInfosRequestDropped,
              weak_ptr_factory_.GetWeakPtr(), service_connection)));
}

void ServiceVideoCaptureProvider::OnDeviceInfosReceived(
    scoped_refptr<RefCountedVideoSourceProvider> service_connection,
    GetSourceInfosResult result,
    const std::vector<media::VideoCaptureDeviceInfo>& infos) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  LogGetDeviceInfosResult(result, get_device_infos_retried_);
  for (GetDeviceInfosCallback& callback : get_device_infos_pending_callbacks_) {
    media::mojom::DeviceEnumerationResult callback_result;
    switch (result) {
      case GetSourceInfosResult::kSuccess:
        callback_result = media::mojom::DeviceEnumerationResult::kSuccess;
        break;
      case GetSourceInfosResult::kErrorDroppedRequest:
        callback_result = media::mojom::DeviceEnumerationResult::
            kErrorCaptureServiceDroppedRequest;
        break;
      default:
        NOTREACHED() << "Invalid GetSourceInfosResult result " << result;
    }
    std::move(callback).Run(callback_result, infos);
  }
  get_device_infos_pending_callbacks_.clear();
}

void ServiceVideoCaptureProvider::OnDeviceInfosRequestDropped(
    scoped_refptr<RefCountedVideoSourceProvider> service_connection) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  if (base::FeatureList::IsEnabled(
          features::kRetryGetVideoCaptureDeviceInfos)) {
    if (!get_device_infos_retried_) {
      get_device_infos_retried_ = true;
      // Do nothing, OnServiceStopped will retry the query automatically when
      // the service has been torn down.
      return;
    }
    LOG(WARNING) << "Too many GetDeviceInfos() retries";
    emit_log_message_cb_.Run(
        "ServiceVideoCaptureProvider::OnDeviceInfosRequestDropped: Too many "
        "retries");
  }

  LogGetDeviceInfosResult(/*get_source_infos_result=*/std::nullopt,
                          get_device_infos_retried_);

  // After too many retries, we just return an empty list
  for (GetDeviceInfosCallback& callback : get_device_infos_pending_callbacks_) {
    std::move(callback).Run(
        media::mojom::DeviceEnumerationResult::kErrorCaptureServiceCrash,
        std::vector<media::VideoCaptureDeviceInfo>());
  }
  get_device_infos_pending_callbacks_.clear();
}

void ServiceVideoCaptureProvider::OnLostConnectionToSourceProvider() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  emit_log_message_cb_.Run(
      "ServiceVideoCaptureProvider::OnLostConnectionToSourceProvider");
  // 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.
  OnServiceConnectionClosed(ReasonForDisconnect::kConnectionLost);
}

void ServiceVideoCaptureProvider::OnServiceConnectionClosed(
    ReasonForDisconnect reason) {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);

  time_of_last_uninitialize_ = base::TimeTicks::Now();
}

void ServiceVideoCaptureProvider::OnGpuInfoUpdate() {
  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
  if (!weak_service_connection_) {
    // Only need to notify the service if it's already running.
    return;
  }
#if BUILDFLAG(IS_WIN)
  GetVideoCaptureService().OnGpuInfoUpdate(
      content::GpuDataManager::GetInstance()->GetGPUInfo().active_gpu().luid);
#endif
}

}  // namespace content