// Copyright 2019 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_TEST_VIDEO_PLAYER_TEST_VDA_VIDEO_DECODER_H_
#define MEDIA_GPU_TEST_VIDEO_PLAYER_TEST_VDA_VIDEO_DECODER_H_

#include <stdint.h>
#include <map>
#include <memory>

#include "base/containers/lru_cache.h"
#include "base/macros.h"
#include "base/sequence_checker.h"
#include "gpu/ipc/service/gpu_memory_buffer_factory.h"
#include "media/base/video_decoder.h"
#include "media/gpu/test/video_player/video_decoder_client.h"
#include "media/media_buildflags.h"
#include "media/video/video_decode_accelerator.h"

namespace media {

class VideoFrame;

namespace test {

class FrameRenderer;

// The test VDA video decoder translates between the media::VideoDecoder and the
// media::VideoDecodeAccelerator interfaces. This makes it possible to run
// VD-based tests against VDA's.
class TestVDAVideoDecoder : public media::VideoDecoder,
                            public VideoDecodeAccelerator::Client {
 public:
  // Constructor for the TestVDAVideoDecoder.
  TestVDAVideoDecoder(bool use_vd_vda,
                      const gfx::ColorSpace& target_color_space,
                      FrameRenderer* const frame_renderer,
                      gpu::GpuMemoryBufferFactory* gpu_memory_buffer_factory);

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

  ~TestVDAVideoDecoder() override;

  // media::VideoDecoder implementation
  VideoDecoderType GetDecoderType() const override;
  bool IsPlatformDecoder() const override;
  void Initialize(const VideoDecoderConfig& config,
                  bool low_delay,
                  CdmContext* cdm_context,
                  InitCB init_cb,
                  const OutputCB& output_cb,
                  const WaitingCB& waiting_cb) override;
  void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB decode_cb) override;
  void Reset(base::OnceClosure reset_cb) override;
  bool NeedsBitstreamConversion() const override;
  bool CanReadWithoutStalling() const override;
  int GetMaxDecodeRequests() const override;

 private:
  // media::VideoDecodeAccelerator::Client implementation
  void NotifyInitializationComplete(Status status) override;
  void ProvidePictureBuffers(uint32_t requested_num_of_buffers,
                             VideoPixelFormat format,
                             uint32_t textures_per_buffer,
                             const gfx::Size& dimensions,
                             uint32_t texture_target) override;
  void ProvidePictureBuffersWithVisibleRect(uint32_t requested_num_of_buffers,
                                            VideoPixelFormat format,
                                            uint32_t textures_per_buffer,
                                            const gfx::Size& dimensions,
                                            const gfx::Rect& visible_rect,
                                            uint32_t texture_target) override;
  void DismissPictureBuffer(int32_t picture_buffer_id) override;
  void PictureReady(const Picture& picture) override;
  void ReusePictureBufferTask(int32_t picture_buffer_id);
  void NotifyEndOfBitstreamBuffer(int32_t bitstream_buffer_id) override;
  void NotifyFlushDone() override;
  void NotifyResetDone() override;
  void NotifyError(VideoDecodeAccelerator::Error error) override;

  // Helper thunk to avoid dereferencing WeakPtrs on the wrong thread.
  static void ReusePictureBufferThunk(
      absl::optional<base::WeakPtr<TestVDAVideoDecoder>> decoder_client,
      scoped_refptr<base::SequencedTaskRunner> task_runner,
      int32_t picture_buffer_id);

  // Get the next bitstream buffer id to be used.
  int32_t GetNextBitstreamBufferId();
  // Get the next picture buffer id to be used.
  int32_t GetNextPictureBufferId();

  // Called when initialization is done. The callback is stored only when VDA
  // allows deferred initialization.
  InitCB init_cb_;
  // Called when a buffer is decoded.
  OutputCB output_cb_;
  // Called when the decoder finished flushing.
  DecodeCB flush_cb_;
  // Called when the decoder finished resetting.
  base::OnceClosure reset_cb_;

  // Whether VdVideoDecodeAccelerator is used.
  bool use_vd_vda_;

  // Output color space, used as hint to decoder to avoid conversions.
  const gfx::ColorSpace target_color_space_;

  // Frame renderer used to manage GL context.
  FrameRenderer* const frame_renderer_;

#if BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)
  // Owned by VideoDecoderClient.
  gpu::GpuMemoryBufferFactory* const gpu_memory_buffer_factory_;
#endif  // BUILDFLAG(USE_CHROMEOS_MEDIA_ACCELERATION)

  // Map of video frames the decoder uses as output, keyed on picture buffer id.
  std::map<int32_t, scoped_refptr<VideoFrame>> video_frames_;
  // Map of video frame decoded callbacks, keyed on bitstream buffer id.
  std::map<int32_t, DecodeCB> decode_cbs_;
  // Records the time at which each bitstream buffer decode operation started.
  base::LRUCache<int32_t, base::TimeDelta> decode_start_timestamps_;

  int32_t next_bitstream_buffer_id_ = 0;
  int32_t next_picture_buffer_id_ = 0;

  std::unique_ptr<VideoDecodeAccelerator> decoder_;

  scoped_refptr<base::SequencedTaskRunner> vda_wrapper_task_runner_;

  SEQUENCE_CHECKER(vda_wrapper_sequence_checker_);

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

}  // namespace test
}  // namespace media

#endif  // MEDIA_GPU_TEST_VIDEO_PLAYER_TEST_VDA_VIDEO_DECODER_H_
