chromium/ash/components/arc/video_accelerator/gpu_arc_video_frame_pool.h

// 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.

#ifndef ASH_COMPONENTS_ARC_VIDEO_ACCELERATOR_GPU_ARC_VIDEO_FRAME_POOL_H_
#define ASH_COMPONENTS_ARC_VIDEO_ACCELERATOR_GPU_ARC_VIDEO_FRAME_POOL_H_

#include <optional>
#include <vector>

#include "ash/components/arc/mojom/video_frame_pool.mojom.h"
#include "ash/components/arc/video_accelerator/video_frame_plane.h"
#include "base/files/scoped_file.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "media/base/status.h"
#include "media/base/video_frame.h"
#include "media/base/video_frame_layout.h"
#include "media/gpu/chromeos/vda_video_frame_pool.h"
#include "mojo/public/cpp/bindings/associated_receiver.h"
#include "mojo/public/cpp/bindings/associated_remote.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_associated_remote.h"
#include "ui/gfx/gpu_memory_buffer.h"

namespace arc {

class ProtectedBufferManager;

// Arc video frame pool mojo service, managing a set of video frames for a
// remote mojo client. The client is responsible for creating and adding video
// frames to the pool, so they can be used by the associated video decoder.
// Note: The video frame pool is currently implemented as a client of the
//       VdaVideoFramePool. Once the wrapper-based solution used in the
//       GpuArcVideoDecodeAccelerator has been deprecated the VdaVideoFramePool
//       can be rolled into this class.
class GpuArcVideoFramePool : public mojom::VideoFramePool,
                             public media::VdaVideoFramePool::VdaDelegate {
 public:
  GpuArcVideoFramePool(
      mojo::PendingAssociatedReceiver<mojom::VideoFramePool> video_frame_pool,
      base::RepeatingClosure release_client_video_frames_cb,
      scoped_refptr<ProtectedBufferManager> protected_buffer_manager);
  ~GpuArcVideoFramePool() override;

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

  // Implementation of mojom::VideoFramePool.
  void Initialize(mojo::PendingAssociatedRemote<mojom::VideoFramePoolClient>
                      client) override;
  void AddVideoFrame(mojom::VideoFramePtr video_frame,
                     AddVideoFrameCallback callback) override;

  // The GpuArcVideoDecoder should call this method when it's about to call
  // Reset() on the underlying VideoDecoder. This is useful to interrupt a
  // request for frames if a reset request comes in the middle of it.
  void WillResetDecoder();

  // The GpuArcVideoDecoder should call this method when a VideoDecoder::Reset()
  // request has been completed. This is needed to start handling requests for
  // frames after a WillResetDecoder() call.
  void OnDecoderResetDone();

  // Implementation of VdaVideoFramePool::VdaDelegate.
  // RequestFrames will be called upon initialization and resolution changes.
  // The request is forwarded to the mojo client using RequestVideoFrames(). The
  // client then calls AddVideoFrame() N times to populate the pool.
  void RequestFrames(const media::Fourcc& fourcc,
                     const gfx::Size& coded_size,
                     const gfx::Rect& visible_rect,
                     size_t max_num_frames,
                     NotifyLayoutChangedCb notify_layout_changed_cb,
                     ImportFrameCb import_frame_cb) override;
  media::VideoFrame::StorageType GetFrameStorageType() const override;

  // Get the mojo frame ID associated with the specified |video_frame|.
  std::optional<int32_t> GetVideoFrameId(const media::VideoFrame* video_frame);

  // Called when all references to a video frame have been dropped.
  void OnFrameReleased(scoped_refptr<media::FrameResource> origin_frame);

  // Get a weak reference to the video frame pool.
  base::WeakPtr<GpuArcVideoFramePool> WeakThis() { return weak_this_; }

 private:
  // Create a GPU memory handle from the specified |fd|.
  gfx::GpuMemoryBufferHandle CreateGpuMemoryHandle(
      base::ScopedFD fd,
      const std::vector<VideoFramePlane>& planes,
      media::VideoPixelFormat pixel_format,
      uint64_t modifier);

  // Create a frame from the specified |gmb_handle|.
  scoped_refptr<media::FrameResource> CreateFrame(
      gfx::GpuMemoryBufferHandle gmb_handle,
      media::VideoPixelFormat pixel_format) const;

  // Submits a RequestFrames() call to |pool_client_|.
  void HandleRequestFrames(const media::Fourcc& fourcc,
                           const gfx::Size& coded_size,
                           const gfx::Rect& visible_rect,
                           size_t max_num_frames);

  // Checks if it's time to submit the next previously deferred RequestFrames()
  // call to the remote end.
  void CallPendingRequestFrames();

  void OnRequestVideoFramesDone();

  void Stop();

  // The local video frame pool mojo service.
  mojo::AssociatedReceiver<mojom::VideoFramePool> video_frame_pool_receiver_;
  // The remote video frame pool mojo client.
  mojo::AssociatedRemote<mojom::VideoFramePoolClient> pool_client_;
  std::optional<uint32_t> pool_client_version_;

  // callback used to notify the video frame pool of new video frame formats,
  // used when the pool requests new frames using RequestFrames().
  NotifyLayoutChangedCb notify_layout_changed_cb_;
  // Callback used to insert new frames in the video frame pool.
  ImportFrameCb import_frame_cb_;
  // Callback used to notify the video decoder that it should release its
  // references to the client video frames as the pool will no longer track
  // them.
  base::RepeatingClosure release_client_video_frames_cb_;

  // The coded size currently used for video frames.
  gfx::Size coded_size_;

  // Map of video frame buffer ids to the associated video frame ids.
  std::map<gfx::GenericSharedMemoryId, int32_t> buffer_id_to_video_frame_id_;

  // The protected buffer manager, used when decoding an encrypted video.
  scoped_refptr<ProtectedBufferManager> protected_buffer_manager_;
  // Whether we're decoding an encrypted video.
  std::optional<bool> secure_mode_;

  // If true, this pool is waiting from ACK message from the client after
  // calling mojom::VideoFramePoolClient::RequestVideoFrames().
  bool awaiting_request_frames_ack_ GUARDED_BY_CONTEXT(sequence_checker_) =
      false;
  static constexpr uint32_t kMinVersionForRequestFramesAck = 1;

  // This queue tracks calls to RequestFrames() that can't be submitted to the
  // remote end yet because we're still waiting for an ACK for a previous
  // |pool_client_|->RequestVideoFrames() call.
  //
  // The reason to hold off calling RequestVideoFrames() while waiting on an ACK
  // for a previous call is a specific corner case: suppose we get a Reset()
  // while waiting for an ACK. In that case, we'll unblock the underlying
  // VdaVideoFramePool by calling |notify_layout_changed_cb_| with
  // kResetRequired. Then the decoder might produce another request for frames.
  // However, we are still waiting for an incoming ACK for the first request
  // (the one that was aborted). Until then, it's expected that we don't submit
  // the second request. This expectation was inferred from
  // https://crrev.com/c/3070325 which does something analogous but for the
  // legacy VDA stack.
  //
  // This queue is intended to be used together with
  // |awaiting_request_frames_ack_|.
  std::queue<base::OnceClosure> pending_frame_requests_
      GUARDED_BY_CONTEXT(sequence_checker_);

  // When |decoder_is_resetting_| is true, the decoder is in the middle of a
  // Reset() so we should reject requests for frames.
  bool decoder_is_resetting_ GUARDED_BY_CONTEXT(sequence_checker_) = false;

  bool has_error_ GUARDED_BY_CONTEXT(sequence_checker_) = false;

  // The client task runner and its sequence checker. All methods should be run
  // on this task runner.
  scoped_refptr<base::SequencedTaskRunner> client_task_runner_;
  SEQUENCE_CHECKER(sequence_checker_);

  base::WeakPtr<GpuArcVideoFramePool> weak_this_;
  base::WeakPtrFactory<GpuArcVideoFramePool> weak_this_factory_{this};
};

}  // namespace arc

#endif  // ASH_COMPONENTS_ARC_VIDEO_ACCELERATOR_GPU_ARC_VIDEO_FRAME_POOL_H_