// Copyright 2012 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_BASE_DECODER_BUFFER_H_
#define MEDIA_BASE_DECODER_BUFFER_H_

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <utility>

#include "base/check.h"
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "base/memory/raw_span.h"
#include "base/memory/read_only_shared_memory_region.h"
#include "base/memory/ref_counted.h"
#include "base/memory/shared_memory_mapping.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/time/time.h"
#include "base/types/pass_key.h"
#include "build/build_config.h"
#include "media/base/decoder_buffer_side_data.h"
#include "media/base/decrypt_config.h"
#include "media/base/media_export.h"
#include "media/base/timestamp_constants.h"

namespace media {

// A specialized buffer for interfacing with audio / video decoders.
//
// Also includes decoder specific functionality for decryption.
//
// NOTE: It is illegal to call any method when end_of_stream() is true.
class MEDIA_EXPORT DecoderBuffer
    : public base::RefCountedThreadSafe<DecoderBuffer> {
 public:
  REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE();

  // ExternalMemory wraps a class owning a buffer and expose the data interface
  // through Span(). This class is derived by a class that owns the class owning
  // the buffer owner class.
  struct MEDIA_EXPORT ExternalMemory {
   public:
    virtual ~ExternalMemory() = default;
    virtual const base::span<const uint8_t> Span() const = 0;
  };

  using DiscardPadding = DecoderBufferSideData::DiscardPadding;

  // TODO(crbug.com/365814210): Remove this structure. It's barely used outside
  // of unit tests.
  struct MEDIA_EXPORT TimeInfo {
    // Presentation time of the frame.
    base::TimeDelta timestamp;

    // Presentation duration of the frame.
    base::TimeDelta duration;

    // Duration of (audio) samples from the beginning and end of this frame
    // which should be discarded after decoding. A value of kInfiniteDuration
    // for the first value indicates the entire frame should be discarded; the
    // second value must be base::TimeDelta() in this case.
    DiscardPadding discard_padding;
  };

  // Allocates buffer with |size| > 0. |is_key_frame_| will default to false.
  // If size is 0, no buffer will be allocated.
  // TODO(crbug.com/365814210): Remove this constructor. Clients should use the
  // FromArray constructor instead asking for a writable DecoderBuffer.
  explicit DecoderBuffer(size_t size);

  // Allocates a buffer with a copy of `data` in it. `is_key_frame_` will
  // default to false.
  DecoderBuffer(base::PassKey<DecoderBuffer>, base::span<const uint8_t> data);
  DecoderBuffer(base::PassKey<DecoderBuffer>, base::HeapArray<uint8_t> data);
  DecoderBuffer(base::PassKey<DecoderBuffer>,
                std::unique_ptr<ExternalMemory> external_memory);
  enum class DecoderBufferType { kNormal, kEndOfStream };
  using ConfigVariant = DecoderBufferSideData::ConfigVariant;
  DecoderBuffer(base::PassKey<DecoderBuffer>,
                DecoderBufferType decoder_buffer_type,
                std::optional<ConfigVariant> next_config);
  DecoderBuffer(const DecoderBuffer&) = delete;
  DecoderBuffer& operator=(const DecoderBuffer&) = delete;

  // Create a DecoderBuffer whose |data_| is copied from |data|. The buffer's
  // |is_key_frame_| will default to false.
  static scoped_refptr<DecoderBuffer> CopyFrom(base::span<const uint8_t> data);

  // Create a DecoderBuffer where data() of |size| bytes resides within the heap
  // as byte array. The buffer's |is_key_frame_| will default to false.
  //
  // Ownership of |data| is transferred to the buffer.
  static scoped_refptr<DecoderBuffer> FromArray(base::HeapArray<uint8_t> data);

  // Create a DecoderBuffer where data() of |size| bytes resides within the
  // memory referred to by |region| at non-negative offset |offset|. The
  // buffer's |is_key_frame_| will default to false.
  //
  // The shared memory will be mapped read-only.
  //
  // If mapping fails, nullptr will be returned.
  static scoped_refptr<DecoderBuffer> FromSharedMemoryRegion(
      base::UnsafeSharedMemoryRegion region,
      uint64_t offset,
      size_t size);

  // Create a DecoderBuffer where data() of |size| bytes resides within the
  // ReadOnlySharedMemoryRegion referred to by |mapping| at non-negative offset
  // |offset|. The buffer's |is_key_frame_| will default to false.
  //
  // Ownership of |region| is transferred to the buffer.
  static scoped_refptr<DecoderBuffer> FromSharedMemoryRegion(
      base::ReadOnlySharedMemoryRegion region,
      uint64_t offset,
      size_t size);

  // Creates a DecoderBuffer with ExternalMemory. The buffer accessed through
  // the created DecoderBuffer is |span| of |external_memory||.
  // |external_memory| is owned by DecoderBuffer until it is destroyed.
  static scoped_refptr<DecoderBuffer> FromExternalMemory(
      std::unique_ptr<ExternalMemory> external_memory);

  // Create a DecoderBuffer indicating we've reached end of stream. If this is
  // an EOS buffer for a config change, the upcoming config may optionally be
  // provided to allow the decoder to make more optimal configuration decisions.
  //
  // Calling any method other than end_of_stream() or next_config() on the
  // resulting buffer is disallowed.
  static scoped_refptr<DecoderBuffer> CreateEOSBuffer(
      std::optional<ConfigVariant> next_config = std::nullopt);

  // Method to verify if subsamples of a DecoderBuffer match.
  static bool DoSubsamplesMatch(const DecoderBuffer& buffer);

  // TODO(crbug.com/365814210): Remove this method.
  TimeInfo time_info() const {
    DCHECK(!end_of_stream());
    return {timestamp_, duration_, discard_padding()};
  }

  base::TimeDelta timestamp() const {
    DCHECK(!end_of_stream());
    return timestamp_;
  }

  // TODO(crbug.com/365814210): This should be renamed at some point, but to
  // avoid a yak shave keep as a virtual with hacker_style() for now.
  virtual void set_timestamp(base::TimeDelta timestamp);

  base::TimeDelta duration() const {
    DCHECK(!end_of_stream());
    return duration_;
  }

  void set_duration(base::TimeDelta duration) {
    DCHECK(!end_of_stream());
    DCHECK(duration == kNoTimestamp ||
           (duration >= base::TimeDelta() && duration != kInfiniteDuration))
        << duration.InSecondsF();
    duration_ = duration;
  }

  // The pointer to the start of the buffer. Prefer to construct a span around
  // the buffer, such as `base::span(decoder_buffer)`.
  // TODO(crbug.com/365814210): Remove in favor of AsSpan().
  const uint8_t* data() const {
    DCHECK(!end_of_stream());
    if (external_memory_)
      return external_memory_->Span().data();
    return data_.data();
  }

  // The number of bytes in the buffer.
  size_t size() const {
    DCHECK(!end_of_stream());
    return external_memory_ ? external_memory_->Span().size() : data_.size();
  }

  // Prefer writable_span(), though it should also be removed.
  //
  // TODO(crbug.com/41383992): Remove writable_data().
  uint8_t* writable_data() const {
    DCHECK(!end_of_stream());
    DCHECK(!external_memory_);
    return const_cast<uint8_t*>(data_.data());
  }

  // TODO(crbug.com/41383992): Remove writable_span().
  base::span<uint8_t> writable_span() const {
    // TODO(crbug.com/40284755): `data_` should be converted to HeapArray, then
    // it can give out a span safely.
    return UNSAFE_TODO(base::span(writable_data(), size()));
  }

  bool empty() const {
    return external_memory_ ? external_memory_->Span().empty() : data_.empty();
  }

  // Read-only iteration as bytes. This allows this type to meet the
  // requirements of `std::ranges::contiguous_range`, and thus be implicitly
  // convertible to a span.
  auto begin() const {
    return external_memory_ ? external_memory_->Span().begin() : data_.begin();
  }
  auto end() const {
    return external_memory_ ? external_memory_->Span().end() : data_.end();
  }
  auto first(size_t count) const {
    return external_memory_ ? external_memory_->Span().first(count)
                            : data_.first(count);
  }
  auto subspan(size_t offset, size_t count) const {
    return external_memory_ ? external_memory_->Span().subspan(offset, count)
                            : data_.subspan(offset, count);
  }

  // TODO(crbug.com/365814210): Change the return type to std::optional.
  DiscardPadding discard_padding() const {
    DCHECK(!end_of_stream());
    return side_data_ ? side_data_->discard_padding : DiscardPadding();
  }

  // TODO(crbug.com/365814210): Remove this method and force callers to get it
  // through side_data().
  void set_discard_padding(const DiscardPadding& discard_padding);

  // Returns DecryptConfig associated with |this|. Returns null if |this| is
  // not encrypted.
  const DecryptConfig* decrypt_config() const {
    DCHECK(!end_of_stream());
    return decrypt_config_.get();
  }

  // TODO(b/331652782): integrate the setter function into the constructor to
  // make |decrypt_config_| immutable.
  void set_decrypt_config(std::unique_ptr<DecryptConfig> decrypt_config) {
    DCHECK(!end_of_stream());
    decrypt_config_ = std::move(decrypt_config);
  }

  bool end_of_stream() const { return is_end_of_stream_; }

  bool is_key_frame() const {
    DCHECK(!end_of_stream());
    return is_key_frame_;
  }

  bool is_encrypted() const {
    DCHECK(!end_of_stream());
    return decrypt_config() && decrypt_config()->encryption_scheme() !=
                                   EncryptionScheme::kUnencrypted;
  }

  void set_is_key_frame(bool is_key_frame) {
    DCHECK(!end_of_stream());
    is_key_frame_ = is_key_frame;
  }

  // Returns DecoderBufferSideData associated with `this`. Check if `side_data_`
  // exists using `has_side_data()` before calling this function.
  const DecoderBufferSideData* side_data() const {
    DCHECK(!end_of_stream());
    return side_data_.get();
  }

  // TODO(b/331652782): integrate the setter function into the constructor to
  // make |side_data_| immutable.
  DecoderBufferSideData& WritableSideData();
  void set_side_data(std::unique_ptr<DecoderBufferSideData> side_data);

  // Returns true if all fields in |buffer| matches this buffer including
  // |data_|.
  bool MatchesForTesting(const DecoderBuffer& buffer) const;

  // As above, except that |data_| is not compared.
  bool MatchesMetadataForTesting(const DecoderBuffer& buffer) const;

  // Returns a human-readable string describing |*this|.
  std::string AsHumanReadableString(bool verbose = false) const;

  // Returns total memory usage for both bookkeeping and buffered data. The
  // function is added for more accurately memory management.
  virtual size_t GetMemoryUsage() const;

  // Accessor for DecoderBufferSideData::next_config.
  std::optional<ConfigVariant> next_config() const {
    DCHECK(end_of_stream());
    return side_data_ ? side_data_->next_config : std::nullopt;
  }

 protected:
  friend class base::RefCountedThreadSafe<DecoderBuffer>;
  virtual ~DecoderBuffer();

  // Allocates a buffer with a copy of `data` in it. `is_key_frame_` will
  // default to false.
  explicit DecoderBuffer(base::span<const uint8_t> data);
  explicit DecoderBuffer(base::HeapArray<uint8_t> data);
  explicit DecoderBuffer(std::unique_ptr<ExternalMemory> external_memory);
  DecoderBuffer(DecoderBufferType decoder_buffer_type,
                std::optional<ConfigVariant> next_config);

  // Encoded data, if it is stored on the heap.
  const base::HeapArray<uint8_t> data_;

 private:
  // ***************************************************************************
  // WARNING: This is a highly allocated object. Care should be taken when
  // adding any fields to make sure they are absolutely necessary. If a field
  // must be added and can be optional, ensure it is heap allocated through the
  // usage of something like std::unique_ptr.
  // ***************************************************************************

  // Presentation time of the frame.
  base::TimeDelta timestamp_;

  // Presentation duration of the frame.
  base::TimeDelta duration_;

  // Structured side data.
  std::unique_ptr<DecoderBufferSideData> side_data_;

  const std::unique_ptr<ExternalMemory> external_memory_;

  // Encryption parameters for the encoded data.
  std::unique_ptr<DecryptConfig> decrypt_config_;

  // Whether the frame was marked as a keyframe in the container.
  bool is_key_frame_ : 1 = false;

  // Whether the buffer represent the end of stream.
  const bool is_end_of_stream_ : 1 = false;
};

}  // namespace media

#endif  // MEDIA_BASE_DECODER_BUFFER_H_