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

#include "media/audio/flac_audio_handler.h"

#include <algorithm>
#include <cstdlib>
#include <cstring>

#include "base/check.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/logging.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_fifo.h"
#include "media/base/audio_sample_types.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/limits.h"
#include "third_party/flac/include/FLAC/ordinals.h"
#include "third_party/flac/include/FLAC/stream_decoder.h"

namespace media {

FlacAudioHandler::FlacAudioHandler(std::string_view data)
    : flac_data_(data), decoder_(FLAC__stream_decoder_new()) {}

FlacAudioHandler::~FlacAudioHandler() = default;

bool FlacAudioHandler::Initialize() {
  DCHECK(decoder_);
  DCHECK(!is_initialized());

  // Initialize the `decoder_`.
  const FLAC__StreamDecoderInitStatus init_status =
      FLAC__stream_decoder_init_stream(
          /*decoder=*/decoder_.get(), /*read_callback=*/ReadCallback,
          /*seek_callback=*/nullptr, /*tell_callback=*/nullptr,
          /*length_callback=*/nullptr, /*eof_callback=*/nullptr,
          /*write_callback=*/WriteCallback, /*metadata_callback=*/MetaCallback,
          /*error_callback=*/ErrorCallback,
          /*client_data=*/static_cast<void*>(this));
  DCHECK_EQ(init_status, FLAC__STREAM_DECODER_INIT_STATUS_OK);

  // Get the metadata.
  if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder_.get())) {
    return false;
  }
  // Avoid that `fifo_` will be unbounded if metadata isn't in the first
  // packet.
  Reset();
  return is_initialized();
}

int FlacAudioHandler::GetNumChannels() const {
  DCHECK(is_initialized());
  return num_channels_;
}

int FlacAudioHandler::GetSampleRate() const {
  DCHECK(is_initialized());
  return sample_rate_;
}

base::TimeDelta FlacAudioHandler::GetDuration() const {
  DCHECK(is_initialized());
  return AudioTimestampHelper::FramesToTime(static_cast<int64_t>(total_frames_),
                                            sample_rate_);
}

bool FlacAudioHandler::AtEnd() const {
  auto state = FLAC__stream_decoder_get_state(decoder_.get());
  return state ==
             FLAC__StreamDecoderState::FLAC__STREAM_DECODER_END_OF_STREAM ||
         state == FLAC__StreamDecoderState::FLAC__STREAM_DECODER_ABORTED ||
         has_error_;
}

bool FlacAudioHandler::CopyTo(AudioBus* bus, size_t* frames_written) {
  CHECK(bus);
  CHECK(is_initialized());
  CHECK_EQ(bus->frames(), kDefaultFrameCount);

  return CopyPartialFramesTo(bus, bus->frames(), /*bus_start_frame=*/0,
                             frames_written);
}

bool FlacAudioHandler::CopyPartialFramesTo(AudioBus* bus,
                                           int frame_count,
                                           int bus_start_frame,
                                           size_t* frames_written) {
  CHECK(bus);
  CHECK(is_initialized());
  CHECK_EQ(bus->channels(), num_channels_);
  CHECK_LE(bus_start_frame + frame_count, bus->frames());
  CHECK_GE(frame_count, 0);
  CHECK_GE(bus_start_frame, 0);

  if (AtEnd()) {
    CHECK_EQ(fifo_->frames(), 0u);
    bus->ZeroFramesPartial(bus_start_frame, frame_count);
    *frames_written = 0;
    return true;
  }

  // Records the number of frames copied into `bus`.
  size_t frames_copied = 0;
  size_t frames_to_copy = static_cast<size_t>(frame_count);

  do {
    if (fifo_->frames() == 0 && !AtEnd()) {
      write_callback_called_ = false;
      if (!FLAC__stream_decoder_process_single(decoder_.get())) {
        return false;
      }
    }

    if (fifo_->frames() > 0u) {
      const size_t frames = std::min(frames_to_copy - frames_copied,
                                     static_cast<size_t>(fifo_->frames()));

      // Consume writes to `bus` starting at the requested offset
      fifo_->Consume(bus, bus_start_frame + frames_copied, frames);
      frames_copied += frames;
    }
  } while (!AtEnd() && frames_copied < frames_to_copy);

  if (frames_copied < frames_to_copy) {
    bus->ZeroFramesPartial(bus_start_frame + frames_copied,
                           frames_to_copy - frames_copied);
  }

  *frames_written = frames_copied;
  return true;
}

void FlacAudioHandler::Reset() {
  FLAC__stream_decoder_reset(decoder_.get());
  if (is_initialized()) {
    fifo_->Clear();
  }
  cursor_ = 0;
  has_error_ = false;
}

FLAC__StreamDecoderReadStatus FlacAudioHandler::ReadCallback(
    const FLAC__StreamDecoder* decoder,
    FLAC__byte buffer[],
    size_t* bytes,
    void* client_data) {
  // SAFETY:
  // https://xiph.org/flac/api/group__flac__stream__decoder.html#ga25d4321dc2f122d35ddc9061f44beae7
  // says `buffer` is the address of the buffer to be decoded and `bytes` points
  // to the number of bytes the buffer can hold.
  UNSAFE_BUFFERS(
      base::span<uint8_t> output(static_cast<uint8_t*>(buffer), *bytes));
  return reinterpret_cast<FlacAudioHandler*>(client_data)
      ->ReadCallbackInternal(output, bytes);
}

FLAC__StreamDecoderWriteStatus FlacAudioHandler::WriteCallback(
    const FLAC__StreamDecoder* decoder,
    const FLAC__Frame* frame,
    FLAC_Channels buffer[],
    void* client_data) {
  const size_t num_channels = frame->header.channels;
  // SAFETY:
  // https://xiph.org/flac/api/group__flac__stream__decoder.html#ga61e48dc2c0d2f6c5519290ff046874a4
  // says `buffer` is an array of pointers to the decoded audio (one for each
  // channel).
  UNSAFE_BUFFERS(
      base::span output(static_cast<FLAC_Channels*>(buffer), num_channels));
  return reinterpret_cast<FlacAudioHandler*>(client_data)
      ->WriteCallbackInternal(frame, output);
}

void FlacAudioHandler::MetaCallback(const FLAC__StreamDecoder* decoder,
                                    const FLAC__StreamMetadata* metadata,
                                    void* client_data) {
  reinterpret_cast<FlacAudioHandler*>(client_data)
      ->MetaCallbackInternal(metadata);
}

void FlacAudioHandler::ErrorCallback(const FLAC__StreamDecoder* decoder,
                                     FLAC__StreamDecoderErrorStatus status,
                                     void* client_data) {
  // `FLAC__STREAM_DECODER_ERROR_STATUS_BAD_METADATA` is the last enumeration
  // value of `FLAC__StreamDecoderErrorStatus`. The legal status value should be
  // less than or equal to it.
  CHECK_LE(status, FLAC__STREAM_DECODER_ERROR_STATUS_BAD_METADATA);
  LOG(ERROR) << "Got an error callback: "
             // SAFETY: The above CHECK ensures that `status` does not exceed
             // the length of `FLAC__StreamDecoderErrorStatusString`
             << UNSAFE_BUFFERS(FLAC__StreamDecoderErrorStatusString[status]);
  reinterpret_cast<FlacAudioHandler*>(client_data)->ErrorCallbackInternal();
}

FLAC__StreamDecoderReadStatus FlacAudioHandler::ReadCallbackInternal(
    base::span<uint8_t> buffer,
    size_t* bytes) {
  // Abort to avoid a deadlock.
  if (buffer.empty()) {
    return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
  }

  DCHECK_LE(cursor_, flac_data_.size());
  // Check if there is enough data to read.
  const size_t available_data_size = flac_data_.size() - cursor_;
  if (available_data_size < buffer.size()) {
    buffer = buffer.first(available_data_size);
    *bytes = available_data_size;
  }

  buffer.copy_from_nonoverlapping(
      base::as_byte_span(flac_data_).subspan(cursor_, *bytes));

  // Update `cursor_`.
  cursor_ += *bytes;

  // Check for end of input.
  return cursor_ >= flac_data_.size()
             ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM
             : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
}

FLAC__StreamDecoderWriteStatus FlacAudioHandler::WriteCallbackInternal(
    const FLAC__Frame* frame,
    const base::span<FLAC_Channels> buffer) {
  // For some fuzzer cases (b/41495570), a single call of
  // `FLAC__stream_decoder_process_single` will trigger the write callback for
  // multiple times to add silence frames. We don't support the abnormal padding
  // configurations.
  if (has_error_ || write_callback_called_) {
    return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
  }

  // Get the number of channels and the number of samples per channel.
  const size_t num_samples = frame->header.blocksize;

  // Avoid the crash happened in `fifo_`.
  if (buffer.size() != static_cast<size_t>(num_channels_)) {
    return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
  }

  // Discard the packet if there are more than the number of `max_blocksize`
  // frames.
  if (num_samples > static_cast<size_t>(bus_->frames())) {
    return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
  }

  for (size_t ch = 0; ch < buffer.size(); ++ch) {
    auto channel_data = bus_->channel(ch);
    // SAFETY:
    // https://xiph.org/flac/api/group__flac__stream__decoder.html#ga61e48dc2c0d2f6c5519290ff046874a4
    // says that each buffer pointer will point to a signed array of samples of
    // length `num_samples`.
    UNSAFE_BUFFERS(base::span source(buffer[ch], num_samples));
    std::ranges::transform(source, channel_data.begin(),
                           SignedInt16SampleTypeTraits::ToFloat);
  }

  fifo_->Push(bus_.get(), num_samples);
  write_callback_called_ = true;

  return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

void FlacAudioHandler::MetaCallbackInternal(
    const FLAC__StreamMetadata* metadata) {
  DCHECK(metadata);
  if (metadata->type != FLAC__METADATA_TYPE_STREAMINFO) {
    return;
  }

  // Avoid re-initialize the metadata variables due to some bad data.
  if (is_initialized()) {
    return;
  }

  // Stores the metadata for the audio.
  num_channels_ = static_cast<int>(metadata->data.stream_info.channels);
  sample_rate_ = static_cast<int>(metadata->data.stream_info.sample_rate);
  total_frames_ = metadata->data.stream_info.total_samples;
  bits_per_sample_ =
      static_cast<int>(metadata->data.stream_info.bits_per_sample);

  // We will create `fifo_` only when the params are valid.
  if (!AreParamsValid()) {
    LOG(ERROR) << "****Format is invalid. "
               << "num_channels: " << num_channels_ << " "
               << "sample_rate: " << sample_rate_ << " "
               << "bits_per_sample: " << bits_per_sample_ << " "
               << "total_frames_: " << total_frames_;
    return;
  }

  int max_blocksize =
      static_cast<int>(metadata->data.stream_info.max_blocksize);
  if (max_blocksize <= 0) {
    return;
  }

  // There will be at most `max_blocksize` of frames for each call to the
  // `WriteCallback()`. For the client bus in `CopyTo()`, it will always
  // have `kDefaultFrameCount` frames. We want to make sure the `fifo_`
  // can hold enough data to refill for a request. For example, we might
  // have `N
  // - 1` frames in the `fifo_` when a request for `N` frames comes in.
  // Then we need to fill the new coming `N` frames into `fifo_`, as a
  // result it requires a total capacity of `N + N - 1`. Technically, it
  // can be `kDefaultFrameCount + kDefaultFrameCount - 1` and
  // `max_blocksize + kDefaultFrameCount - 1`. 2 is just used for
  // simplicity.
  fifo_ = std::make_unique<AudioFifo>(
      num_channels_, std::max(kDefaultFrameCount * 2, max_blocksize * 2));

  bus_ = AudioBus::Create(num_channels_, max_blocksize);
}

void FlacAudioHandler::ErrorCallbackInternal() {
  has_error_ = true;
}

bool FlacAudioHandler::AreParamsValid() const {
  return (num_channels_ > 0 &&
          num_channels_ <= static_cast<int>(limits::kMaxChannels)) &&
         (bits_per_sample_ == 16) &&
         (sample_rate_ >= static_cast<int>(limits::kMinSampleRate) &&
          sample_rate_ <= static_cast<int>(limits::kMaxSampleRate)) &&
         (total_frames_ != 0);
}

}  // namespace media
