chromium/media/mojo/services/media_foundation_renderer_wrapper.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/mojo/services/media_foundation_renderer_wrapper.h"

#include "base/functional/callback_helpers.h"
#include "base/task/sequenced_task_runner.h"
#include "media/base/win/mf_helpers.h"
#include "media/mojo/mojom/renderer_extensions.mojom.h"
#include "media/mojo/services/media_foundation_gpu_info_monitor.h"
#include "media/mojo/services/mojo_media_log.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "mojo/public/cpp/system/platform_handle.h"

namespace media {

namespace {

bool HasAudio(MediaResource* media_resource) {
  DCHECK(media_resource->GetType() == MediaResource::Type::kStream);

  const auto media_streams = media_resource->GetAllStreams();
  for (const media::DemuxerStream* stream : media_streams) {
    if (stream->type() == media::DemuxerStream::Type::AUDIO)
      return true;
  }

  return false;
}

LUID ChromeLuidToLuid(const CHROME_LUID& chrome_luid) {
  LUID luid;
  luid.LowPart = chrome_luid.LowPart;
  luid.HighPart = chrome_luid.HighPart;
  return luid;
}

}  // namespace

MediaFoundationRendererWrapper::MediaFoundationRendererWrapper(
    scoped_refptr<base::SequencedTaskRunner> task_runner,
    mojom::FrameInterfaceFactory* frame_interfaces,
    mojo::PendingRemote<mojom::MediaLog> media_log_remote,
    mojo::PendingReceiver<RendererExtension> renderer_extension_receiver,
    mojo::PendingRemote<ClientExtension> client_extension_remote)
    : frame_interfaces_(frame_interfaces),
      renderer_extension_receiver_(this,
                                   std::move(renderer_extension_receiver)),
      client_extension_remote_(std::move(client_extension_remote), task_runner),
      site_mute_observer_(this) {
  DVLOG_FUNC(1);
  DCHECK(frame_interfaces_);

  renderer_ = std::make_unique<MediaFoundationRenderer>(
      std::move(task_runner),
      std::make_unique<MojoMediaLog>(std::move(media_log_remote), task_runner),
      ChromeLuidToLuid(
          MediaFoundationGpuInfoMonitor::GetInstance()->gpu_luid()));

  luid_update_subscription_ =
      MediaFoundationGpuInfoMonitor::GetInstance()->AddLuidObserver(
          base::BindRepeating(&MediaFoundationRendererWrapper::OnGpuLuidChange,
                              weak_factory_.GetWeakPtr()));
}

MediaFoundationRendererWrapper::~MediaFoundationRendererWrapper() {
  DVLOG_FUNC(1);
  if (!dcomp_surface_token_.is_empty())
    dcomp_surface_registry_->UnregisterDCOMPSurfaceHandle(dcomp_surface_token_);
}

void MediaFoundationRendererWrapper::Initialize(
    MediaResource* media_resource,
    RendererClient* client,
    PipelineStatusCallback init_cb) {
  if (HasAudio(media_resource)) {
    frame_interfaces_->RegisterMuteStateObserver(
        site_mute_observer_.BindNewPipeAndPassRemote());
  }

  renderer_->SetFrameReturnCallbacks(
      base::BindRepeating(
          &MediaFoundationRendererWrapper::OnFrameGeneratedByMediaFoundation,
          weak_factory_.GetWeakPtr()),
      base::BindRepeating(
          &MediaFoundationRendererWrapper::OnFramePoolInitialized,
          weak_factory_.GetWeakPtr()));

  renderer_->Initialize(media_resource, client, std::move(init_cb));
}

void MediaFoundationRendererWrapper::SetCdm(CdmContext* cdm_context,
                                            CdmAttachedCB cdm_attached_cb) {
  renderer_->SetCdm(cdm_context, std::move(cdm_attached_cb));
}

void MediaFoundationRendererWrapper::SetLatencyHint(
    std::optional<base::TimeDelta> latency_hint) {
  renderer_->SetLatencyHint(latency_hint);
}

void MediaFoundationRendererWrapper::Flush(base::OnceClosure flush_cb) {
  renderer_->Flush(std::move(flush_cb));
}

void MediaFoundationRendererWrapper::StartPlayingFrom(base::TimeDelta time) {
  renderer_->StartPlayingFrom(time);
}

void MediaFoundationRendererWrapper::SetPlaybackRate(double playback_rate) {
  renderer_->SetPlaybackRate(playback_rate);
}

void MediaFoundationRendererWrapper::SetVolume(float volume) {
  volume_ = volume;
  renderer_->SetVolume(muted_ ? 0 : volume_);
}

base::TimeDelta MediaFoundationRendererWrapper::GetMediaTime() {
  return renderer_->GetMediaTime();
}

RendererType MediaFoundationRendererWrapper::GetRendererType() {
  return RendererType::kMediaFoundation;
}

void MediaFoundationRendererWrapper::GetDCOMPSurface(
    GetDCOMPSurfaceCallback callback) {
  if (has_get_dcomp_surface_called_) {
    renderer_extension_receiver_.ReportBadMessage(
        "GetDCOMPSurface should only be called once!");
    return;
  }

  has_get_dcomp_surface_called_ = true;
  renderer_->GetDCompSurface(
      base::BindOnce(&MediaFoundationRendererWrapper::OnReceiveDCOMPSurface,
                     weak_factory_.GetWeakPtr(), std::move(callback)));
}

void MediaFoundationRendererWrapper::SetVideoStreamEnabled(bool enabled) {
  renderer_->SetVideoStreamEnabled(enabled);
}

void MediaFoundationRendererWrapper::SetOutputRect(
    const gfx::Rect& output_rect,
    SetOutputRectCallback callback) {
  renderer_->SetOutputRect(output_rect, std::move(callback));
}

void MediaFoundationRendererWrapper::OnMuteStateChange(bool muted) {
  DVLOG_FUNC(2) << ": muted=" << muted;

  if (muted == muted_)
    return;

  muted_ = muted;
  renderer_->SetVolume(muted_ ? 0 : volume_);
}

void MediaFoundationRendererWrapper::OnGpuLuidChange(
    const CHROME_LUID& adapter_luid) {
  renderer_->SetGpuProcessAdapterLuid(ChromeLuidToLuid(adapter_luid));
}

void MediaFoundationRendererWrapper::OnReceiveDCOMPSurface(
    GetDCOMPSurfaceCallback callback,
    base::win::ScopedHandle handle,
    const std::string& error) {
  if (!handle.IsValid()) {
    std::move(callback).Run(std::nullopt, "invalid handle: " + error);
    return;
  }

  if (!dcomp_surface_registry_) {
    frame_interfaces_->CreateDCOMPSurfaceRegistry(
        dcomp_surface_registry_.BindNewPipeAndPassReceiver());
  }

  auto register_cb = base::BindOnce(
      &MediaFoundationRendererWrapper::OnDCOMPSurfaceHandleRegistered,
      weak_factory_.GetWeakPtr(), std::move(callback));

  dcomp_surface_registry_->RegisterDCOMPSurfaceHandle(
      mojo::PlatformHandle(std::move(handle)),
      mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(register_cb),
                                                  std::nullopt));
}

void MediaFoundationRendererWrapper::OnDCOMPSurfaceHandleRegistered(
    GetDCOMPSurfaceCallback callback,
    const std::optional<base::UnguessableToken>& token) {
  std::string error;
  if (token) {
    DCHECK(dcomp_surface_token_.is_empty());
    dcomp_surface_token_ = token.value();
  } else {
    error = "dcomp surface handle registration failed";
  }

  std::move(callback).Run(token, error);
}

void MediaFoundationRendererWrapper::OnFramePoolInitialized(
    std::vector<MediaFoundationFrameInfo> frame_textures,
    const gfx::Size& texture_size) {
  auto pool_params = media::mojom::FramePoolInitializationParameters::New();
  for (auto& texture : frame_textures) {
    auto frame_info = media::mojom::FrameTextureInfo::New();
    gfx::GpuMemoryBufferHandle gpu_handle;

    gpu_handle.dxgi_handle = std::move(texture.dxgi_handle);
    gpu_handle.dxgi_token = gfx::DXGIHandleToken();
    gpu_handle.type = gfx::GpuMemoryBufferType::DXGI_SHARED_HANDLE;

    frame_info->token = texture.token;
    frame_info->texture_handle = std::move(gpu_handle);
    pool_params->frame_textures.emplace_back(std::move(frame_info));
  }

  pool_params->texture_size = texture_size;
  client_extension_remote_->InitializeFramePool(std::move(pool_params));
}

void MediaFoundationRendererWrapper::OnFrameGeneratedByMediaFoundation(
    const base::UnguessableToken& frame_token,
    const gfx::Size& frame_size,
    base::TimeDelta frame_timestamp) {
  client_extension_remote_->OnFrameAvailable(frame_token, frame_size,
                                             frame_timestamp);
}

void MediaFoundationRendererWrapper::NotifyFrameReleased(
    const base::UnguessableToken& frame_token) {
  renderer_->NotifyFrameReleased(frame_token);
}

void MediaFoundationRendererWrapper::RequestNextFrame() {
  renderer_->RequestNextFrame();
}

void MediaFoundationRendererWrapper::SetMediaFoundationRenderingMode(
    MediaFoundationRenderingMode mode) {
  renderer_->SetMediaFoundationRenderingMode(mode);
}
}  // namespace media