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

#ifndef MEDIA_GPU_VAAPI_ACCELERATED_VIDEO_ENCODER_H_
#define MEDIA_GPU_VAAPI_ACCELERATED_VIDEO_ENCODER_H_

#include <vector>

#include "base/callback.h"
#include "base/containers/queue.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
#include "media/base/video_bitrate_allocation.h"
#include "media/base/video_codecs.h"
#include "media/gpu/codec_picture.h"
#include "ui/gfx/geometry/size.h"

namespace media {

struct BitstreamBufferMetadata;
class VaapiEncodeJob;
class VideoFrame;

// An AcceleratedVideoEncoder (AVE) performs high-level, platform-independent
// encoding process tasks, such as managing codec state, reference frames, etc.,
// but may require support from an external accelerator (typically a hardware
// accelerator) to offload some stages of the actual encoding process, using
// the parameters that AVE prepares beforehand.
//
// For each frame to be encoded, clients provide an EncodeJob object to be set
// up by an AVE with job parameters, and execute the job afterwards. Any
// resources required for the job are also provided by the clients, and
// associated with the EncodeJob object.
class AcceleratedVideoEncoder {
 public:
  AcceleratedVideoEncoder() = default;
  virtual ~AcceleratedVideoEncoder() = default;

  // An abstraction of an encode job for one frame. Parameters required for an
  // EncodeJob to be executed are prepared by an AcceleratedVideoEncoder, while
  // the accelerator-specific callbacks required to set up and execute it are
  // provided by the accelerator itself, based on these parameters.
  // Accelerators are also responsible for providing any resources (such as
  // memory for output and reference pictures, etc.) as needed.
  class EncodeJob : public base::RefCounted<EncodeJob> {
   public:
    // Creates an EncodeJob to encode |input_frame|, which will be executed
    // by calling |execute_cb|. If |keyframe| is true, requests this job
    // to produce a keyframe.
    EncodeJob(scoped_refptr<VideoFrame> input_frame,
              bool keyframe,
              base::OnceClosure execute_cb);

    // Schedules a callback to be run immediately before this job is executed.
    // Can be called multiple times to schedule multiple callbacks, and all
    // of them will be run, in order added.
    // Callbacks can be used to e.g. set up hardware parameters before the job
    // is executed.
    void AddSetupCallback(base::OnceClosure cb);

    // Adds |ref_pic| to the list of pictures to be used as reference pictures
    // for this frame, to ensure they remain valid until the job is executed
    // (or discarded).
    void AddReferencePicture(scoped_refptr<CodecPicture> ref_pic);

    // Runs all setup callbacks previously scheduled, if any, in order added,
    // and executes the job by calling the execute callback. Note that the
    // actual job execution may be asynchronous, and returning from this method
    // does not have to indicate that the job has been finished. The execute
    // callback is responsible for retaining references to any resources that
    // may be in use after this method returns however, so it is safe to release
    // the EncodeJob object itself immediately after this method returns.
    void Execute();

    // Requests this job to produce a keyframe; requesting a keyframe may not
    // always result in one being produced by the encoder (e.g. if it would
    // not fit in the bitrate budget).
    void ProduceKeyframe() { keyframe_ = true; }

    // Returns true if this job has been requested to produce a keyframe.
    bool IsKeyframeRequested() const { return keyframe_; }

    // Returns the timestamp associated with this job.
    base::TimeDelta timestamp() const { return timestamp_; }

    virtual BitstreamBufferMetadata Metadata(size_t payload_size) const;

    virtual VaapiEncodeJob* AsVaapiEncodeJob();

   protected:
    friend class base::RefCounted<EncodeJob>;
    virtual ~EncodeJob();

   private:
    // Input VideoFrame to be encoded.
    const scoped_refptr<VideoFrame> input_frame_;

    // Source timestamp for |input_frame_|.
    const base::TimeDelta timestamp_;

    // True if this job is to produce a keyframe.
    bool keyframe_;

    // Callbacks to be run (in the same order as the order of AddSetupCallback()
    // calls) to set up the job.
    base::queue<base::OnceClosure> setup_callbacks_;

    // Callback to be run to execute this job.
    base::OnceClosure execute_callback_;

    // Reference pictures required for this job.
    std::vector<scoped_refptr<CodecPicture>> reference_pictures_;

    DISALLOW_COPY_AND_ASSIGN(EncodeJob);
  };

  // Initializes the encoder to encode frames of |visible_size| into a stream
  // for |profile|, at |initial_bitrate| and |initial_framerate|.
  // Returns false if the requested set of parameters is not supported,
  // true on success.
  virtual bool Initialize(const gfx::Size& visible_size,
                          VideoCodecProfile profile,
                          uint32_t initial_bitrate,
                          uint32_t initial_framerate) = 0;

  // Updates current framerate and/or bitrate to |framerate| in FPS
  // and the specified video bitrate allocation.
  virtual bool UpdateRates(const VideoBitrateAllocation& bitrate_allocation,
                           uint32_t framerate) = 0;

  // Returns coded size for the input buffers required to encode, in pixels;
  // typically visible size adjusted to match codec alignment requirements.
  virtual gfx::Size GetCodedSize() const = 0;

  // Returns minimum size in bytes for bitstream buffers required to fit output
  // stream buffers produced.
  virtual size_t GetBitstreamBufferSize() const = 0;

  // Returns maximum number of reference frames that may be used by the
  // encoder to encode one frame. The client should be able to provide up to
  // at least this many frames simultaneously for encode to make progress.
  virtual size_t GetMaxNumOfRefFrames() const = 0;

  // Prepares a new |encode_job| to be executed in Accelerator and returns true
  // on success. The caller may then call Execute() on the job to run it.
  virtual bool PrepareEncodeJob(EncodeJob* encode_job) = 0;
};

}  // namespace media

#endif  // MEDIA_GPU_VAAPI_ACCELERATED_VIDEO_ENCODER_H_
