chromium/chromecast/media/cma/decoder/external_audio_decoder_wrapper.cc

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

#include "chromecast/media/cma/decoder/external_audio_decoder_wrapper.h"

#include <algorithm>
#include <utility>

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/scoped_native_library.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"
#include "chromecast/media/api/decoder_buffer_base.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_sample_types.h"
#include "media/base/decoder_buffer.h"

namespace chromecast {
namespace media {

namespace {

const char kDefaultExternalDecoderPath[] = "libcast_external_decoder.so";

const char kSupportedConfigFunction[] =
    "ExternalAudioDecoder_IsSupportedConfig";
const char kCreateFunction[] = "ExternalAudioDecoder_CreateDecoder";
const char kDeleteFunction[] = "ExternalAudioDecoder_DeleteDecoder";

const size_t kMinConversionBufferSize = 256;

class ExternalDecoderLib {
 public:
  ExternalDecoderLib() : lib_(base::FilePath(kDefaultExternalDecoderPath)) {
    if (lib_.is_valid()) {
      supported_config_func_ = reinterpret_cast<IsSupportedConfigFunction>(
          lib_.GetFunctionPointer(kSupportedConfigFunction));
      create_func_ = reinterpret_cast<CreateFunction>(
          lib_.GetFunctionPointer(kCreateFunction));
      delete_func_ = reinterpret_cast<DeleteFunction>(
          lib_.GetFunctionPointer(kDeleteFunction));

      LOG_IF(ERROR, !supported_config_func_)
          << "Missing function: " << kSupportedConfigFunction;
      LOG_IF(ERROR, !create_func_) << "Missing function: " << kCreateFunction;
      LOG_IF(ERROR, !delete_func_) << "Missing function: " << kDeleteFunction;
    }
  }

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

  ~ExternalDecoderLib() = default;

  bool IsSupportedConfig(const AudioConfig& config) {
    if (!supported_config_func_ || !create_func_ || !delete_func_) {
      return false;
    }

    return supported_config_func_(config);
  }

  ExternalAudioDecoder* CreateDecoder(
      ExternalAudioDecoder::Delegate* delegate,
      const chromecast::media::AudioConfig& config) {
    if (!create_func_ || !delete_func_) {
      return nullptr;
    }

    return create_func_(delegate, config);
  }

  void DeleteDecoder(ExternalAudioDecoder* decoder) {
    DCHECK(delete_func_);
    delete_func_(decoder);
  }

 private:
  using IsSupportedConfigFunction =
      decltype(&ExternalAudioDecoder_IsSupportedConfig);
  using CreateFunction = decltype(&ExternalAudioDecoder_CreateDecoder);
  using DeleteFunction = decltype(&ExternalAudioDecoder_DeleteDecoder);

  base::ScopedNativeLibrary lib_;
  IsSupportedConfigFunction supported_config_func_ = nullptr;
  CreateFunction create_func_ = nullptr;
  DeleteFunction delete_func_ = nullptr;
};

ExternalDecoderLib& GetLib() {
  static base::NoDestructor<ExternalDecoderLib> g_lib;
  return *g_lib;
}

AudioConfig BuildOutputConfig(const AudioConfig& input_config,
                              CastAudioDecoder::OutputFormat output_format,
                              ExternalAudioDecoder* decoder) {
  AudioConfig output_config = input_config;
  output_config.encryption_scheme = EncryptionScheme::kUnencrypted;
  output_config.codec = kCodecPCM;
  output_config.sample_format =
      (output_format == CastAudioDecoder::kOutputSigned16
           ? kSampleFormatS16
           : kSampleFormatPlanarF32);
  output_config.channel_number = decoder->GetNumOutputChannels();
  if (output_config.channel_number <= 0) {
    output_config.channel_number = input_config.channel_number;
  }
  return output_config;
}

}  // namespace

class ExternalAudioDecoderWrapper::DecodedBuffer : public DecoderBufferBase {
 public:
  DecodedBuffer(StreamId stream_id, size_t capacity)
      : stream_id_(stream_id),
        capacity_(capacity),
        data_(std::make_unique<uint8_t[]>(capacity_)) {}

  void set_size(size_t size) {
    DCHECK_LE(size, capacity_);
    size_ = size;
  }

  // DecoderBufferBase implementation:
  StreamId stream_id() const override { return stream_id_; }
  int64_t timestamp() const override { return timestamp_.InMicroseconds(); }
  void set_timestamp(base::TimeDelta timestamp) override {
    timestamp_ = timestamp;
  }
  const uint8_t* data() const override { return data_.get(); }
  uint8_t* writable_data() const override { return data_.get(); }
  size_t data_size() const override { return size_; }
  const CastDecryptConfig* decrypt_config() const override { return nullptr; }
  bool end_of_stream() const override { return false; }
  bool is_key_frame() const override { return false; }

 private:
  ~DecodedBuffer() override = default;

  const StreamId stream_id_;
  const size_t capacity_;

  const std::unique_ptr<uint8_t[]> data_;

  base::TimeDelta timestamp_;
  size_t size_ = 0;
};

// static
bool ExternalAudioDecoderWrapper::IsSupportedConfig(const AudioConfig& config) {
  return GetLib().IsSupportedConfig(config);
}

ExternalAudioDecoderWrapper::ExternalAudioDecoderWrapper(
    scoped_refptr<base::SequencedTaskRunner> task_runner,
    const AudioConfig& config,
    CastAudioDecoder::OutputFormat output_format)
    : task_runner_(std::move(task_runner)),
      output_format_(output_format),
      decoder_(GetLib().CreateDecoder(this, config)),
      output_config_(BuildOutputConfig(config, output_format_, decoder_)) {}

ExternalAudioDecoderWrapper::~ExternalAudioDecoderWrapper() {
  if (decoder_) {
    GetLib().DeleteDecoder(decoder_);
  }
}

const AudioConfig& ExternalAudioDecoderWrapper::GetOutputConfig() const {
  return output_config_;
}

void ExternalAudioDecoderWrapper::Decode(
    scoped_refptr<media::DecoderBufferBase> data,
    DecodeCallback decode_callback) {
  DCHECK(task_runner_->RunsTasksInCurrentSequence());
  // Post a task, since some callers don't expect a synchronous callback.
  task_runner_->PostTask(
      FROM_HERE, base::BindOnce(&ExternalAudioDecoderWrapper::DecodeDeferred,
                                weak_factory_.GetWeakPtr(), std::move(data),
                                std::move(decode_callback)));
}

void ExternalAudioDecoderWrapper::DecodeDeferred(
    scoped_refptr<media::DecoderBufferBase> data,
    DecodeCallback decode_callback) {
  if (data->end_of_stream()) {
    std::move(decode_callback).Run(kDecodeOk, output_config_, std::move(data));
    return;
  }

  if (!decoder_ || !decoder_->Decode(*data)) {
    std::move(decode_callback).Run(kDecodeError, output_config_, nullptr);
    return;
  }

  size_t buffer_count = buffers_.size() - pending_buffer_;
  scoped_refptr<DecodedBuffer> decoded;
  if (buffer_count == 0) {
    decoded = base::MakeRefCounted<DecodedBuffer>(output_config_.id, 0);
  } else if (buffer_count == 1) {
    decoded = std::move(buffers_.front());
  } else {
    size_t size = 0;
    for (size_t i = 0; i < buffer_count; ++i) {
      size += buffers_[i]->data_size();
    }

    decoded = base::MakeRefCounted<DecodedBuffer>(output_config_.id, size);
    decoded->set_size(size);

    const size_t frame_size = sizeof(float) * output_config_.channel_number;
    size_t total_frames = size / frame_size;
    for (int c = 0; c < output_config_.channel_number; ++c) {
      uint8_t* dest =
          decoded->writable_data() + c * total_frames * sizeof(float);
      for (size_t i = 0; i < buffer_count; ++i) {
        size_t frames = buffers_[i]->data_size() / frame_size;
        void* src = buffers_[i]->writable_data() + c * frames * sizeof(float);
        memcpy(dest, src, frames * sizeof(float));
        dest += frames * sizeof(float);
      }
    }
  }

  buffers_.erase(buffers_.begin(), buffers_.begin() + buffer_count);

  if (output_format_ == CastAudioDecoder::kOutputSigned16) {
    ConvertToS16(decoded.get());
  }

  decoded->set_timestamp(base::Microseconds(data->timestamp()));
  std::move(decode_callback).Run(kDecodeOk, output_config_, std::move(decoded));
}

void ExternalAudioDecoderWrapper::ConvertToS16(DecodedBuffer* buffer) {
  const int channels = output_config_.channel_number;
  const size_t frame_size = sizeof(float) * channels;
  const size_t frames = buffer->data_size() / frame_size;

  if (!conversion_buffer_ ||
      conversion_buffer_->frames() < static_cast<int>(frames) ||
      conversion_buffer_->channels() != channels) {
    conversion_buffer_ = ::media::AudioBus::Create(
        channels, std::max(frames * 2, kMinConversionBufferSize));
  }

  const float* src = reinterpret_cast<const float*>(buffer->data());
  for (int c = 0; c < channels; ++c) {
    std::copy_n(src + c * frames, frames, conversion_buffer_->channel(c));
  }

  int16_t* dest = reinterpret_cast<int16_t*>(buffer->writable_data());
  conversion_buffer_
      ->ToInterleavedPartial<::media::SignedInt16SampleTypeTraits>(0, frames,
                                                                   dest);

  buffer->set_size(frames * channels * sizeof(int16_t));
}

void* ExternalAudioDecoderWrapper::AllocateBuffer(size_t bytes) {
  auto buffer = base::MakeRefCounted<DecodedBuffer>(output_config_.id, bytes);
  void* ptr = buffer->writable_data();
  buffers_.push_back(std::move(buffer));
  pending_buffer_ = true;
  return ptr;
}

void ExternalAudioDecoderWrapper::OnDecodedBuffer(size_t decoded_size_bytes,
                                                  const AudioConfig& config) {
  DCHECK(!buffers_.empty());
  size_t frame_size = sizeof(float) * config.channel_number;
  DCHECK_EQ(decoded_size_bytes % frame_size, 0u);

  buffers_.back()->set_size(decoded_size_bytes);
  output_config_.channel_number = config.channel_number;
  output_config_.samples_per_second = config.samples_per_second;
  pending_buffer_ = false;
}

}  // namespace media
}  // namespace chromecast