// Copyright 2013 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.

#include "content/renderer/media/android/audio_decoder_android.h"

#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <sys/mman.h>
#include <unistd.h>
#include <vector>

#include "base/file_descriptor_posix.h"
#include "base/logging.h"
#include "base/memory/shared_memory.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/process_handle.h"
#include "content/common/view_messages.h"
#include "media/base/android/webaudio_media_codec_info.h"
#include "media/base/audio_bus.h"
#include "media/base/limits.h"
#include "third_party/WebKit/public/platform/WebAudioBus.h"

namespace content {

class AudioDecoderIO {
 public:
  AudioDecoderIO(const char* data, size_t data_size);
  ~AudioDecoderIO();
  bool ShareEncodedToProcess(base::SharedMemoryHandle* handle);

  // Returns true if AudioDecoderIO was successfully created.
  bool IsValid() const;

  int read_fd() const { return read_fd_; }
  int write_fd() const { return write_fd_; }

 private:
  // Shared memory that will hold the encoded audio data.  This is
  // used by MediaCodec for decoding.
  base::SharedMemory encoded_shared_memory_;

  // A pipe used to communicate with MediaCodec.  MediaCodec owns
  // write_fd_ and writes to it.
  int read_fd_;
  int write_fd_;

  DISALLOW_COPY_AND_ASSIGN(AudioDecoderIO);
};

AudioDecoderIO::AudioDecoderIO(const char* data, size_t data_size)
    : read_fd_(-1),
      write_fd_(-1) {

  if (!data || !data_size || data_size > 0x80000000)
    return;

  // Create the shared memory and copy our data to it so that
  // MediaCodec can access it.
  encoded_shared_memory_.CreateAndMapAnonymous(data_size);

  if (!encoded_shared_memory_.memory())
    return;

  memcpy(encoded_shared_memory_.memory(), data, data_size);

  // Create a pipe for reading/writing the decoded PCM data
  int pipefd[2];

  if (pipe(pipefd))
    return;

  read_fd_ = pipefd[0];
  write_fd_ = pipefd[1];
}

AudioDecoderIO::~AudioDecoderIO() {
  // Close the read end of the pipe.  The write end should have been
  // closed by MediaCodec.
  if (read_fd_ >= 0 && close(read_fd_)) {
    DVLOG(1) << "Cannot close read fd " << read_fd_
             << ": " << strerror(errno);
  }
}

bool AudioDecoderIO::IsValid() const {
  return read_fd_ >= 0 && write_fd_ >= 0 &&
      encoded_shared_memory_.memory();
}

bool AudioDecoderIO::ShareEncodedToProcess(base::SharedMemoryHandle* handle) {
  return encoded_shared_memory_.ShareToProcess(base::GetCurrentProcessHandle(),
                                               handle);
}

static float ConvertSampleToFloat(int16_t sample) {
  const float kMaxScale = 1.0f / std::numeric_limits<int16_t>::max();
  const float kMinScale = -1.0f / std::numeric_limits<int16_t>::min();

  return sample * (sample < 0 ? kMinScale : kMaxScale);
}

// A basic WAVE file decoder.  See
// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ for a
// basic guide to the WAVE file format.
class WAVEDecoder {
 public:
  WAVEDecoder(const uint8* data, size_t data_size);
  ~WAVEDecoder();

  // Try to decode the data as a WAVE file.  If the data is a supported
  // WAVE file, |destination_bus| is filled with the decoded data and
  // DecodeWAVEFile returns true.  Otherwise, DecodeWAVEFile returns
  // false.
  bool DecodeWAVEFile(blink::WebAudioBus* destination_bus);

 private:
  // Minimum number of bytes in a WAVE file to hold all of the data we
  // need to interpret it as a WAVE file.
  static const unsigned kMinimumWAVLength = 44;

  // Number of bytes in the chunk ID field.
  static const unsigned kChunkIDLength = 4;

  // Number of bytes in the chunk size field.
  static const unsigned kChunkSizeLength = 4;

  // Number of bytes in the format field of the "RIFF" chunk.
  static const unsigned kFormatFieldLength = 4;

  // Number of bytes in a valid "fmt" chunk.
  static const unsigned kFMTChunkLength = 16;

  // Supported audio format in a WAVE file.
  // TODO(rtoy): Consider supporting other formats here, if necessary.
  static const int16_t kAudioFormatPCM = 1;

  // Maximum number (inclusive) of bytes per sample supported by this
  // decoder.
  static const unsigned kMaximumBytesPerSample = 3;

  // Read an unsigned integer of |length| bytes from |buffer|.  The
  // integer is interpreted as being in little-endian order.
  uint32_t ReadUnsignedInteger(const uint8_t* buffer, size_t length);

  // Read a PCM sample from the WAVE data at |pcm_data|.
  int16_t ReadPCMSample(const uint8_t* pcm_data);

  // Read a WAVE chunk header including the chunk ID and chunk size.
  // Returns false if the header could not be read.
  bool ReadChunkHeader();

  // Read and parse the "fmt" chunk.  Returns false if the fmt chunk
  // could not be read or contained unsupported formats.
  bool ReadFMTChunk();

  // Read data chunk and save it to |destination_bus|.  Returns false
  // if the data chunk could not be read correctly.
  bool CopyDataChunkToBus(blink::WebAudioBus* destination_bus);

  // The WAVE chunk ID that identifies the chunk.
  uint8_t chunk_id_[kChunkIDLength];

  // The number of bytes in the data portion of the chunk.
  size_t chunk_size_;

  // The total number of bytes in the encoded data.
  size_t data_size_;

  // The current position within the WAVE file.
  const uint8_t* buffer_;

  // Points one byte past the end of the in-memory WAVE file.  Used for
  // detecting if we've reached the end of the file.
  const uint8_t* buffer_end_;

  size_t bytes_per_sample_;

  uint16_t number_of_channels_;

  // Sample rate of the WAVE data, in Hz.
  uint32_t sample_rate_;

  DISALLOW_COPY_AND_ASSIGN(WAVEDecoder);
};

WAVEDecoder::WAVEDecoder(const uint8_t* encoded_data, size_t data_size)
    : data_size_(data_size),
      buffer_(encoded_data),
      buffer_end_(encoded_data + 1),
      bytes_per_sample_(0),
      number_of_channels_(0),
      sample_rate_(0) {
  if (buffer_ + data_size > buffer_)
    buffer_end_ = buffer_ + data_size;
}

WAVEDecoder::~WAVEDecoder() {}

uint32_t WAVEDecoder::ReadUnsignedInteger(const uint8_t* buffer,
                                          size_t length) {
  unsigned value = 0;

  if (length == 0 || length > sizeof(value)) {
    DCHECK(false) << "ReadUnsignedInteger: Invalid length: " << length;
    return 0;
  }

  // All integer fields in a WAVE file are little-endian.
  for (size_t k = length; k > 0; --k)
    value = (value << 8) + buffer[k - 1];

  return value;
}

int16_t WAVEDecoder::ReadPCMSample(const uint8_t* pcm_data) {
  uint32_t unsigned_sample = ReadUnsignedInteger(pcm_data, bytes_per_sample_);
  int16_t sample;

  // Convert the unsigned data into a 16-bit PCM sample.
  switch (bytes_per_sample_) {
    case 1:
      sample = (unsigned_sample - 128) << 8;
      break;
    case 2:
      sample = static_cast<int16_t>(unsigned_sample);
      break;
    case 3:
      // Android currently converts 24-bit WAVE data into 16-bit
      // samples by taking the high-order 16 bits without rounding.
      // We do the same here for consistency.
      sample = static_cast<int16_t>(unsigned_sample >> 8);
      break;
    default:
      sample = 0;
      break;
  }
  return sample;
}

bool WAVEDecoder::ReadChunkHeader() {
  if (buffer_ + kChunkIDLength + kChunkSizeLength >= buffer_end_)
    return false;

  memcpy(chunk_id_, buffer_, kChunkIDLength);

  chunk_size_ = ReadUnsignedInteger(buffer_ + kChunkIDLength, kChunkSizeLength);

  // Adjust for padding
  if (chunk_size_ % 2)
    ++chunk_size_;

  // Check for completely bogus chunk size.
  if (chunk_size_ > data_size_)
    return false;

  return true;
}

bool WAVEDecoder::ReadFMTChunk() {
  // The fmt chunk has basic info about the format of the audio
  // data.  Only a basic PCM format is supported.
  if (chunk_size_ < kFMTChunkLength) {
    DVLOG(1) << "FMT chunk too short: " << chunk_size_;
    return 0;
  }

  uint16_t audio_format = ReadUnsignedInteger(buffer_, 2);

  if (audio_format != kAudioFormatPCM) {
    DVLOG(1) << "Audio format not supported: " << audio_format;
    return false;
  }

  number_of_channels_ = ReadUnsignedInteger(buffer_ + 2, 2);
  sample_rate_ = ReadUnsignedInteger(buffer_ + 4, 4);
  unsigned bits_per_sample = ReadUnsignedInteger(buffer_ + 14, 2);

  // Sanity checks.

  if (!number_of_channels_ ||
      number_of_channels_ > media::limits::kMaxChannels) {
    DVLOG(1) << "Unsupported number of channels: " << number_of_channels_;
    return false;
  }

  if (sample_rate_ < media::limits::kMinSampleRate ||
      sample_rate_ > media::limits::kMaxSampleRate) {
    DVLOG(1) << "Unsupported sample rate: " << sample_rate_;
    return false;
  }

  // We only support 8, 16, and 24 bits per sample.
  if (bits_per_sample == 8 || bits_per_sample == 16 || bits_per_sample == 24) {
    bytes_per_sample_ = bits_per_sample / 8;
    return true;
  }

  DVLOG(1) << "Unsupported bits per sample: " << bits_per_sample;
  return false;
}

bool WAVEDecoder::CopyDataChunkToBus(blink::WebAudioBus* destination_bus) {
  // The data chunk contains the audio data itself.
  if (!bytes_per_sample_ || bytes_per_sample_ > kMaximumBytesPerSample) {
    DVLOG(1) << "WARNING: data chunk without preceeding fmt chunk,"
             << " or invalid bytes per sample.";
    return false;
  }

  DVLOG(0) << "Decoding WAVE file: " << number_of_channels_ << " channels, "
           << sample_rate_ << " kHz, "
           << chunk_size_ / bytes_per_sample_ / number_of_channels_
           << " frames, " << 8 * bytes_per_sample_ << " bits/sample";

  // Create the destination bus of the appropriate size and then decode
  // the data into the bus.
  size_t number_of_frames =
      chunk_size_ / bytes_per_sample_ / number_of_channels_;

  destination_bus->initialize(
      number_of_channels_, number_of_frames, sample_rate_);

  for (size_t m = 0; m < number_of_frames; ++m) {
    for (uint16_t k = 0; k < number_of_channels_; ++k) {
      int16_t sample = ReadPCMSample(buffer_);

      buffer_ += bytes_per_sample_;
      destination_bus->channelData(k)[m] = ConvertSampleToFloat(sample);
    }
  }

  return true;
}

bool WAVEDecoder::DecodeWAVEFile(blink::WebAudioBus* destination_bus) {
  // Parse and decode WAVE file. If we can't parse it, return false.

  if (buffer_ + kMinimumWAVLength > buffer_end_) {
    DVLOG(1) << "Buffer too small to contain full WAVE header: ";
    return false;
  }

  // Do we have a RIFF file?
  ReadChunkHeader();
  if (memcmp(chunk_id_, "RIFF", kChunkIDLength) != 0) {
    DVLOG(1) << "RIFF missing";
    return false;
  }
  buffer_ += kChunkIDLength + kChunkSizeLength;

  // Check the format field of the RIFF chunk
  memcpy(chunk_id_, buffer_, kFormatFieldLength);
  if (memcmp(chunk_id_, "WAVE", kFormatFieldLength) != 0) {
    DVLOG(1) << "Invalid WAVE file:  missing WAVE header";
    return false;
  }
  // Advance past the format field
  buffer_ += kFormatFieldLength;

  // We have a WAVE file.  Start parsing the chunks.

  while (buffer_ < buffer_end_) {
    if (!ReadChunkHeader()) {
      DVLOG(1) << "Couldn't read chunk header";
      return false;
    }

    // Consume the chunk ID and chunk size
    buffer_ += kChunkIDLength + kChunkSizeLength;

    // Make sure we can read all chunk_size bytes.
    if (buffer_ + chunk_size_ > buffer_end_) {
      DVLOG(1) << "Insufficient bytes to read chunk of size " << chunk_size_;
      return false;
    }

    if (memcmp(chunk_id_, "fmt ", kChunkIDLength) == 0) {
      if (!ReadFMTChunk())
        return false;
    } else if (memcmp(chunk_id_, "data", kChunkIDLength) == 0) {
      // Return after reading the data chunk, whether we succeeded or
      // not.
      return CopyDataChunkToBus(destination_bus);
    } else {
      // Ignore these chunks that we don't know about.
      DVLOG(0) << "Ignoring WAVE chunk `" << chunk_id_ << "' size "
               << chunk_size_;
    }

    // Advance to next chunk.
    buffer_ += chunk_size_;
  }

  // If we get here, that means we didn't find a data chunk, so we
  // couldn't handle this WAVE file.

  return false;
}

// The number of frames is known so preallocate the destination
// bus and copy the pcm data to the destination bus as it's being
// received.
static void CopyPcmDataToBus(int input_fd,
                             blink::WebAudioBus* destination_bus,
                             size_t number_of_frames,
                             unsigned number_of_channels,
                             double file_sample_rate) {
  destination_bus->initialize(number_of_channels,
                              number_of_frames,
                              file_sample_rate);

  int16_t pipe_data[PIPE_BUF / sizeof(int16_t)];
  size_t decoded_frames = 0;
  size_t current_sample_in_frame = 0;
  ssize_t nread;

  while ((nread = HANDLE_EINTR(read(input_fd, pipe_data, sizeof(pipe_data)))) >
         0) {
    size_t samples_in_pipe = nread / sizeof(int16_t);

    // The pipe may not contain a whole number of frames.  This is
    // especially true if the number of channels is greater than
    // 2. Thus, keep track of which sample in a frame is being
    // processed, so we handle the boundary at the end of the pipe
    // correctly.
    for (size_t m = 0; m < samples_in_pipe; ++m) {
      if (decoded_frames >= number_of_frames)
        break;

      destination_bus->channelData(current_sample_in_frame)[decoded_frames] =
          ConvertSampleToFloat(pipe_data[m]);
      ++current_sample_in_frame;

      if (current_sample_in_frame >= number_of_channels) {
        current_sample_in_frame = 0;
        ++decoded_frames;
      }
    }
  }

  // number_of_frames is only an estimate.  Resize the buffer with the
  // actual number of received frames.
  if (decoded_frames < number_of_frames)
    destination_bus->resizeSmaller(decoded_frames);
}

// The number of frames is unknown, so keep reading and buffering
// until there's no more data and then copy the data to the
// destination bus.
static void BufferAndCopyPcmDataToBus(int input_fd,
                                      blink::WebAudioBus* destination_bus,
                                      unsigned number_of_channels,
                                      double file_sample_rate) {
  int16_t pipe_data[PIPE_BUF / sizeof(int16_t)];
  std::vector<int16_t> decoded_samples;
  ssize_t nread;

  while ((nread = HANDLE_EINTR(read(input_fd, pipe_data, sizeof(pipe_data)))) >
         0) {
    size_t samples_in_pipe = nread / sizeof(int16_t);
    if (decoded_samples.size() + samples_in_pipe > decoded_samples.capacity()) {
      decoded_samples.reserve(std::max(samples_in_pipe,
                                       2 * decoded_samples.capacity()));
    }
    std::copy(pipe_data,
              pipe_data + samples_in_pipe,
              back_inserter(decoded_samples));
  }

  DVLOG(1) << "Total samples read = " << decoded_samples.size();

  // Convert the samples and save them in the audio bus.
  size_t number_of_samples = decoded_samples.size();
  size_t number_of_frames = decoded_samples.size() / number_of_channels;
  size_t decoded_frames = 0;

  destination_bus->initialize(number_of_channels,
                              number_of_frames,
                              file_sample_rate);

  for (size_t m = 0; m < number_of_samples; m += number_of_channels) {
    for (size_t k = 0; k < number_of_channels; ++k) {
      int16_t sample = decoded_samples[m + k];
      destination_bus->channelData(k)[decoded_frames] =
          ConvertSampleToFloat(sample);
    }
    ++decoded_frames;
  }

  // number_of_frames is only an estimate.  Resize the buffer with the
  // actual number of received frames.
  if (decoded_frames < number_of_frames)
    destination_bus->resizeSmaller(decoded_frames);
}

static bool TryWAVEFileDecoder(blink::WebAudioBus* destination_bus,
                               const uint8_t* encoded_data,
                               size_t data_size) {
  WAVEDecoder decoder(encoded_data, data_size);

  return decoder.DecodeWAVEFile(destination_bus);
}

// To decode audio data, we want to use the Android MediaCodec class.
// But this can't run in a sandboxed process so we need initiate the
// request to MediaCodec in the browser.  To do this, we create a
// shared memory buffer that holds the audio data.  We send a message
// to the browser to start the decoder using this buffer and one end
// of a pipe.  The MediaCodec class will decode the data from the
// shared memory and write the PCM samples back to us over a pipe.
bool DecodeAudioFileData(blink::WebAudioBus* destination_bus, const char* data,
                         size_t data_size,
                         scoped_refptr<ThreadSafeSender> sender) {
  // Try to decode the data as a WAVE file first.  If it can't be
  // decoded, use MediaCodec.  See crbug.com/259048.
  if (TryWAVEFileDecoder(
          destination_bus, reinterpret_cast<const uint8_t*>(data), data_size)) {
    return true;
  }

  AudioDecoderIO audio_decoder(data, data_size);

  if (!audio_decoder.IsValid())
    return false;

  base::SharedMemoryHandle encoded_data_handle;
  audio_decoder.ShareEncodedToProcess(&encoded_data_handle);
  base::FileDescriptor fd(audio_decoder.write_fd(), true);

  DVLOG(1) << "DecodeAudioFileData: Starting MediaCodec";

  // Start MediaCodec processing in the browser which will read from
  // encoded_data_handle for our shared memory and write the decoded
  // PCM samples (16-bit integer) to our pipe.

  sender->Send(new ViewHostMsg_RunWebAudioMediaCodec(
      encoded_data_handle, fd, data_size));

  // First, read the number of channels, the sample rate, and the
  // number of frames and a flag indicating if the file is an
  // ogg/vorbis file.  This must be coordinated with
  // WebAudioMediaCodecBridge!
  //
  // If we know the number of samples, we can create the destination
  // bus directly and do the conversion directly to the bus instead of
  // buffering up everything before saving the data to the bus.

  int input_fd = audio_decoder.read_fd();
  struct media::WebAudioMediaCodecInfo info;

  DVLOG(1) << "Reading audio file info from fd " << input_fd;
  ssize_t nread = HANDLE_EINTR(read(input_fd, &info, sizeof(info)));
  DVLOG(1) << "read:  " << nread << " bytes:\n"
           << " 0: number of channels = " << info.channel_count << "\n"
           << " 1: sample rate        = " << info.sample_rate << "\n"
           << " 2: number of frames   = " << info.number_of_frames << "\n";

  if (nread != sizeof(info))
    return false;

  unsigned number_of_channels = info.channel_count;
  double file_sample_rate = static_cast<double>(info.sample_rate);
  size_t number_of_frames = info.number_of_frames;

  // Sanity checks
  if (!number_of_channels ||
      number_of_channels > media::limits::kMaxChannels ||
      file_sample_rate < media::limits::kMinSampleRate ||
      file_sample_rate > media::limits::kMaxSampleRate) {
    return false;
  }

  if (number_of_frames > 0) {
    CopyPcmDataToBus(input_fd,
                     destination_bus,
                     number_of_frames,
                     number_of_channels,
                     file_sample_rate);
  } else {
    BufferAndCopyPcmDataToBus(input_fd,
                              destination_bus,
                              number_of_channels,
                              file_sample_rate);
  }

  return true;
}

}  // namespace content
