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

#ifndef MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_BUFFER_POOL_H_
#define MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_BUFFER_POOL_H_

#include "base/memory/ref_counted.h"
#include "media/capture/capture_export.h"
#include "media/capture/mojom/video_capture_buffer.mojom.h"
#include "media/capture/mojom/video_capture_types.mojom.h"
#include "media/capture/video/video_capture_buffer_pool_constants.h"
#include "media/capture/video/video_capture_device.h"
#include "media/capture/video_capture_types.h"
#include "mojo/public/cpp/system/buffer.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/gpu_memory_buffer_handle.h"

namespace media {

class VideoCaptureBufferHandle;

// A thread-safe class that does the bookkeeping and lifetime management for a
// pool of pixel buffers cycled between an in-process producer (e.g. a
// VideoCaptureDevice) and a set of out-of-process consumers. The pool is
// intended to be orchestrated by a VideoCaptureDevice::Client, but is designed
// to outlive the controller if necessary. The pixel buffers may be backed by a
// SharedMemory, but this is not compulsory.
//
// Producers get a buffer by calling ReserveForProducer(), and may pass on their
// ownership to the consumer by calling HoldForConsumers(), or drop the buffer
// (without further processing) by calling RelinquishProducerReservation().
// Consumers signal that they are done with the buffer by calling
// RelinquishConsumerHold().
//
// Buffers are allocated on demand, but there will never be more than |count|
// buffers in existence at any time. Buffers are identified by an int value
// called |buffer_id|. -1 (kInvalidId) is never a valid ID, and is returned by
// some methods to indicate failure. The active set of buffer ids may change
// over the lifetime of the buffer pool, as existing buffers are freed and
// reallocated at larger size. When reallocation occurs, new buffer IDs will
// circulate.
class CAPTURE_EXPORT VideoCaptureBufferPool
    : public base::RefCountedThreadSafe<VideoCaptureBufferPool> {
 public:
  REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();

  static constexpr int kInvalidId = VideoCaptureBufferPoolConstants::kInvalidId;

  // Provides a duplicate region referring to the buffer. Destruction of this
  // duplicate does not result in releasing the shared memory held by the
  // pool. The buffer will be writable. This may be called as necessary to
  // create regions.
  virtual base::UnsafeSharedMemoryRegion DuplicateAsUnsafeRegion(
      int buffer_id) = 0;

  // Try and obtain a read/write access to the buffer.
  virtual std::unique_ptr<VideoCaptureBufferHandle> GetHandleForInProcessAccess(
      int buffer_id) = 0;

  virtual gfx::GpuMemoryBufferHandle GetGpuMemoryBufferHandle(
      int buffer_id) = 0;

  virtual media::mojom::VideoBufferHandlePtr GetVideoBufferHandle(
      int buffer_id) = 0;

  // Returns the buffer type of the buffer. Useful when deciding whether to
  // serialize the buffer for IPC either as shared memory or GMB.
  virtual VideoCaptureBufferType GetBufferType(int buffer_id) = 0;

  // Reserve or allocate a buffer to support a packed frame of |dimensions| of
  // pixel |format| and return its id. If the pool is already at maximum
  // capacity, this will return kMaxBufferCountExceeded and set |buffer_id| to
  // |kInvalidId|.
  //
  // If successful, the reserved buffer remains reserved (and writable by the
  // producer) until ownership is transferred either to the consumer via
  // HoldForConsumers(), or back to the pool with
  // RelinquishProducerReservation().
  //
  // On occasion, this call will decide to free an old buffer to make room for a
  // new allocation at a larger size. If so, the ID of the destroyed buffer is
  // returned via |buffer_id_to_drop|.
  virtual VideoCaptureDevice::Client::ReserveResult ReserveForProducer(
      const gfx::Size& dimensions,
      VideoPixelFormat format,
      const mojom::PlaneStridesPtr& strides,
      int frame_feedback_id,
      int* buffer_id,
      int* buffer_id_to_drop) = 0;

  // Indicate that a buffer held for the producer should be returned back to the
  // pool without passing on to the consumer. This effectively is the opposite
  // of ReserveForProducer().
  virtual void RelinquishProducerReservation(int buffer_id) = 0;

  // Reserve a buffer id to use for a buffer specified by |handle| (which was
  // allocated by some external source).

  // |buffer.handle| is used to create buffer on windows, mac doesn't create
  // buffer but holds io_surface. |buffer.format| is the source texture format,
  // currently it should be NV12. Buffer tracker will hold |buffer.imf_buffer|
  // for reusing the texture in right timing on Windows since once the
  // imf_buffer is released, Windows capture pipeline assumes the application
  // has finished reading from the texture and the capture pipeline will perform
  // the write operation(i.e. reusing texture). |dimensions| is used for
  // creating buffer on Windows.

  // If the pool is already at maximum capacity, return the reused ID based on
  // LRU strategy. Otherwise, return a new tracker ID via |buffer_id|. The
  // behavior of |buffer_id_to_drop| is the same as ReserveForProducer.
  virtual VideoCaptureDevice::Client::ReserveResult ReserveIdForExternalBuffer(
      CapturedExternalVideoBuffer buffer,
      const gfx::Size& dimensions,
      int* buffer_id_to_drop,
      int* buffer_id) = 0;

  // Returns a snapshot of the current number of buffers in-use divided by the
  // maximum |count_|.
  virtual double GetBufferPoolUtilization() const = 0;

  // Transfer a buffer from producer to consumer ownership.
  // |buffer_id| must be a buffer index previously returned by
  // ReserveForProducer(), and not already passed to HoldForConsumers().
  virtual void HoldForConsumers(int buffer_id, int num_clients) = 0;

  // Indicate that one or more consumers are done with a particular buffer. This
  // effectively is the opposite of HoldForConsumers(). Once the consumers are
  // done, a buffer is returned to the pool for reuse.
  virtual void RelinquishConsumerHold(int buffer_id, int num_clients) = 0;

  // Drop all buffers currently in the pool.
  virtual void InvalidateBuffers() = 0;

 protected:
  friend class base::RefCountedThreadSafe<VideoCaptureBufferPool>;
  virtual ~VideoCaptureBufferPool() = default;
};

}  // namespace media

#endif  // MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_BUFFER_POOL_H_
