chromium/services/video_capture/ash/video_frame_handler_ash.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 "services/video_capture/ash/video_frame_handler_ash.h"

#include <memory>
#include <string>
#include <utility>

#include "base/memory/unsafe_shared_memory_region.h"
#include "base/notreached.h"
#include "media/base/video_transformation.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "mojo/public/cpp/system/buffer.h"
#include "ui/gfx/gpu_memory_buffer.h"

namespace crosapi {

namespace {

crosapi::mojom::ReadyFrameInBufferPtr ToCrosapiBuffer(
    video_capture::mojom::ReadyFrameInBufferPtr buffer,
    scoped_refptr<video_capture::VideoFrameAccessHandlerRemote>
        frame_access_handler_remote) {
  auto crosapi_buffer = crosapi::mojom::ReadyFrameInBuffer::New();
  crosapi_buffer->buffer_id = buffer->buffer_id;
  crosapi_buffer->frame_feedback_id = buffer->frame_feedback_id;

  mojo::PendingRemote<crosapi::mojom::ScopedAccessPermission> access_permission;
  mojo::MakeSelfOwnedReceiver(
      std::make_unique<VideoFrameHandlerAsh::ScopedFrameAccessHandlerNotifier>(
          frame_access_handler_remote, buffer->buffer_id),
      access_permission.InitWithNewPipeAndPassReceiver());
  crosapi_buffer->access_permission = std::move(access_permission);

  const auto& buffer_info = buffer->frame_info;
  auto crosapi_buffer_info = crosapi::mojom::VideoFrameInfo::New();
  crosapi_buffer_info->timestamp = buffer_info->timestamp;
  crosapi_buffer_info->pixel_format = buffer_info->pixel_format;
  crosapi_buffer_info->coded_size = buffer_info->coded_size;
  crosapi_buffer_info->visible_rect = buffer_info->visible_rect;

  auto transformation = buffer_info->metadata.transformation;
  if (transformation) {
    crosapi::mojom::VideoRotation crosapi_rotation;
    switch (transformation->rotation) {
      case media::VideoRotation::VIDEO_ROTATION_0:
        crosapi_rotation = crosapi::mojom::VideoRotation::kVideoRotation0;
        break;
      case media::VideoRotation::VIDEO_ROTATION_90:
        crosapi_rotation = crosapi::mojom::VideoRotation::kVideoRotation90;
        break;
      case media::VideoRotation::VIDEO_ROTATION_180:
        crosapi_rotation = crosapi::mojom::VideoRotation::kVideoRotation180;
        break;
      case media::VideoRotation::VIDEO_ROTATION_270:
        crosapi_rotation = crosapi::mojom::VideoRotation::kVideoRotation270;
        break;
      default:
        NOTREACHED_IN_MIGRATION()
            << "Unexpected rotation in video frame metadata";
    }
    crosapi_buffer_info->rotation = crosapi_rotation;
  }
  if (buffer_info->metadata.reference_time.has_value()) {
    crosapi_buffer_info->reference_time = *buffer_info->metadata.reference_time;
  }

  crosapi_buffer->frame_info = std::move(crosapi_buffer_info);
  return crosapi_buffer;
}

crosapi::mojom::GpuMemoryBufferHandlePtr ToCrosapiGpuMemoryBufferHandle(
    gfx::GpuMemoryBufferHandle buffer_handle) {
  auto crosapi_gpu_handle = crosapi::mojom::GpuMemoryBufferHandle::New();
  crosapi_gpu_handle->id = buffer_handle.id.id;
  crosapi_gpu_handle->offset = buffer_handle.offset;
  crosapi_gpu_handle->stride = buffer_handle.stride;

  if (buffer_handle.type == gfx::GpuMemoryBufferType::SHARED_MEMORY_BUFFER) {
    crosapi_gpu_handle->platform_handle =
        crosapi::mojom::GpuMemoryBufferPlatformHandle::NewSharedMemoryHandle(
            std::move(buffer_handle.region));
  } else if (buffer_handle.type == gfx::GpuMemoryBufferType::NATIVE_PIXMAP) {
    auto crosapi_native_pixmap_handle =
        crosapi::mojom::NativePixmapHandle::New();
    crosapi_native_pixmap_handle->planes =
        std::move(buffer_handle.native_pixmap_handle.planes);
    crosapi_native_pixmap_handle->modifier =
        buffer_handle.native_pixmap_handle.modifier;
    crosapi_gpu_handle->platform_handle =
        crosapi::mojom::GpuMemoryBufferPlatformHandle::NewNativePixmapHandle(
            std::move(crosapi_native_pixmap_handle));
  }
  return crosapi_gpu_handle;
}

}  // namespace

VideoFrameHandlerAsh::VideoFrameHandlerAsh(
    mojo::PendingReceiver<video_capture::mojom::VideoFrameHandler>
        handler_receiver,
    mojo::PendingRemote<crosapi::mojom::VideoFrameHandler> proxy_remote)
    : proxy_(std::move(proxy_remote)) {
  receiver_.Bind(std::move(handler_receiver));
}

VideoFrameHandlerAsh::~VideoFrameHandlerAsh() = default;

VideoFrameHandlerAsh::ScopedFrameAccessHandlerNotifier::
    ScopedFrameAccessHandlerNotifier(
        scoped_refptr<video_capture::VideoFrameAccessHandlerRemote>
            frame_access_handler_remote,
        int32_t buffer_id)
    : frame_access_handler_remote_(std::move(frame_access_handler_remote)),
      buffer_id_(buffer_id) {}

VideoFrameHandlerAsh::ScopedFrameAccessHandlerNotifier::
    ~ScopedFrameAccessHandlerNotifier() {
  (*frame_access_handler_remote_)->OnFinishedConsumingBuffer(buffer_id_);
}

void VideoFrameHandlerAsh::OnCaptureConfigurationChanged() {
  proxy_->OnCaptureConfigurationChanged();
}

void VideoFrameHandlerAsh::OnNewBuffer(
    int buffer_id,
    media::mojom::VideoBufferHandlePtr buffer_handle) {
  crosapi::mojom::VideoBufferHandlePtr crosapi_handle;

  if (buffer_handle->is_unsafe_shmem_region()) {
    crosapi_handle = crosapi::mojom::VideoBufferHandle::NewSharedBufferHandle(
        mojo::WrapPlatformSharedMemoryRegion(
            base::UnsafeSharedMemoryRegion::TakeHandleForSerialization(
                std::move(buffer_handle->get_unsafe_shmem_region()))));
  } else if (buffer_handle->is_gpu_memory_buffer_handle()) {
    crosapi_handle =
        crosapi::mojom::VideoBufferHandle::NewGpuMemoryBufferHandle(
            ToCrosapiGpuMemoryBufferHandle(
                std::move(buffer_handle->get_gpu_memory_buffer_handle())));
  } else if (buffer_handle->is_read_only_shmem_region()) {
    // Lacros is guaranteed to be newer than us so it's okay to skip the version
    // check here.
    crosapi_handle = crosapi::mojom::VideoBufferHandle::NewReadOnlyShmemRegion(
        std::move(buffer_handle->get_read_only_shmem_region()));
  } else {
    NOTREACHED_IN_MIGRATION() << "Unexpected new buffer type";
  }
  proxy_->OnNewBuffer(buffer_id, std::move(crosapi_handle));
}

void VideoFrameHandlerAsh::OnFrameAccessHandlerReady(
    mojo::PendingRemote<video_capture::mojom::VideoFrameAccessHandler>
        pending_frame_access_handler) {
  DCHECK(!frame_access_handler_remote_);
  frame_access_handler_remote_ =
      base::MakeRefCounted<video_capture::VideoFrameAccessHandlerRemote>(
          mojo::Remote<video_capture::mojom::VideoFrameAccessHandler>(
              std::move(pending_frame_access_handler)));
}

void VideoFrameHandlerAsh::OnFrameReadyInBuffer(
    video_capture::mojom::ReadyFrameInBufferPtr buffer) {
  DCHECK(frame_access_handler_remote_);
  crosapi::mojom::ReadyFrameInBufferPtr crosapi_buffer =
      ToCrosapiBuffer(std::move(buffer), frame_access_handler_remote_);

  proxy_->OnFrameReadyInBuffer(std::move(crosapi_buffer));
}

void VideoFrameHandlerAsh::OnBufferRetired(int buffer_id) {
  proxy_->OnBufferRetired(buffer_id);
}

void VideoFrameHandlerAsh::OnError(media::VideoCaptureError error) {
  proxy_->OnError(error);
}

void VideoFrameHandlerAsh::OnFrameDropped(
    media::VideoCaptureFrameDropReason reason) {
  proxy_->OnFrameDropped(reason);
}

void VideoFrameHandlerAsh::OnNewSubCaptureTargetVersion(
    uint32_t sub_capture_target_version) {
  proxy_->OnNewSubCaptureTargetVersion(sub_capture_target_version);
}

void VideoFrameHandlerAsh::OnFrameWithEmptyRegionCapture() {
  proxy_->OnFrameWithEmptyRegionCapture();
}

void VideoFrameHandlerAsh::OnLog(const std::string& message) {
  proxy_->OnLog(message);
}

void VideoFrameHandlerAsh::OnStarted() {
  proxy_->OnStarted();
}

void VideoFrameHandlerAsh::OnStartedUsingGpuDecode() {
  proxy_->OnStartedUsingGpuDecode();
}

void VideoFrameHandlerAsh::OnStopped() {
  proxy_->OnStopped();
}

}  // namespace crosapi