chromium/chromecast/media/common/audio_decoder_software_wrapper.cc

// Copyright 2015 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/common/audio_decoder_software_wrapper.h"

#include <ostream>

#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/task/single_thread_task_runner.h"
#include "chromecast/media/api/decoder_buffer_base.h"
#include "chromecast/media/common/base/decoder_config_logging.h"

namespace chromecast {
namespace media {

namespace {
const int kMonoChannelCount = 1;
const int kStereoChannelCount = 2;
const int k5_1ChannelCount = 6;

bool IsChannelLayoutSupported(AudioConfig config) {
  if (config.channel_number == kMonoChannelCount ||
      config.channel_number == kStereoChannelCount)
    return true;

  // Only supports 5.1 for Opus.
  if (config.channel_number == k5_1ChannelCount &&
      config.codec == AudioCodec::kCodecOpus)
    return true;

  return false;
}

// Codecs that cannot be decoded on the device and must be passed through.
constexpr media::AudioCodec kPassthroughCodecs[] = {
    kCodecEAC3, kCodecAC3, kCodecDTS, kCodecDTSXP2, kCodecMpegHAudio,
};

}  // namespace

AudioDecoderSoftwareWrapper::AudioDecoderSoftwareWrapper(
    MediaPipelineBackend::AudioDecoder* backend_decoder)
    : backend_decoder_(backend_decoder),
      delegate_(nullptr),
      decoder_error_(false) {
  DCHECK(backend_decoder_);
  backend_decoder_->SetDelegate(this);
}

AudioDecoderSoftwareWrapper::~AudioDecoderSoftwareWrapper() {}

void AudioDecoderSoftwareWrapper::SetDelegate(DecoderDelegate* delegate) {
  DCHECK(delegate);
  delegate_ = delegate;
  if (decoder_error_) {
    delegate_->OnDecoderError();
  }
}

MediaPipelineBackend::BufferStatus AudioDecoderSoftwareWrapper::PushBuffer(
    CastDecoderBuffer* buffer) {
  DCHECK(buffer);
  if (!software_decoder_)
    return backend_decoder_->PushBuffer(buffer);

  DecoderBufferBase* buffer_base = static_cast<DecoderBufferBase*>(buffer);
  software_decoder_->Decode(
      base::WrapRefCounted(buffer_base),
      base::BindOnce(&AudioDecoderSoftwareWrapper::OnDecodedBuffer,
                     base::Unretained(this)));
  return MediaPipelineBackend::kBufferPending;
}

void AudioDecoderSoftwareWrapper::GetStatistics(Statistics* statistics) {
  DCHECK(statistics);
  return backend_decoder_->GetStatistics(statistics);
}

bool AudioDecoderSoftwareWrapper::SetConfig(const AudioConfig& config) {
  DCHECK(IsValidConfig(config));

  if (backend_decoder_->SetConfig(config)) {
    LOG(INFO) << "Using backend decoder for " << config.codec;
    software_decoder_.reset();
    output_config_ = config;
    return true;
  }

  if (base::Contains(kPassthroughCodecs, config.codec)) {
    LOG(INFO) << "Cannot use software decoder for " << config.codec;
    return false;
  }

  if (!CreateSoftwareDecoder(config)) {
    LOG(INFO) << "Failed to create software decoder for " << config.codec;
    return false;
  }

  LOG(INFO) << "Using software decoder for " << config.codec;

  output_config_ = software_decoder_->GetOutputConfig();
  return backend_decoder_->SetConfig(output_config_);
}

bool AudioDecoderSoftwareWrapper::SetVolume(float multiplier) {
  return backend_decoder_->SetVolume(multiplier);
}

AudioDecoderSoftwareWrapper::RenderingDelay
AudioDecoderSoftwareWrapper::GetRenderingDelay() {
  return backend_decoder_->GetRenderingDelay();
}

AudioDecoderSoftwareWrapper::AudioTrackTimestamp
AudioDecoderSoftwareWrapper::GetAudioTrackTimestamp() {
  return backend_decoder_->GetAudioTrackTimestamp();
}

int AudioDecoderSoftwareWrapper::GetStartThresholdInFrames() {
  return backend_decoder_->GetStartThresholdInFrames();
}

bool AudioDecoderSoftwareWrapper::IsUsingSoftwareDecoder() {
  return software_decoder_.get() != nullptr;
}

bool AudioDecoderSoftwareWrapper::CreateSoftwareDecoder(
    const AudioConfig& config) {
  if (!IsChannelLayoutSupported(config)) {
    LOG(ERROR) << "Software audio decoding is not supported for channel: "
               << config.channel_number << " with codec: " << config.codec;
    return false;
  }
  // TODO(kmackay) Consider using planar float instead.
  software_decoder_ = media::CastAudioDecoder::Create(
      base::SingleThreadTaskRunner::GetCurrentDefault(), config,
      media::CastAudioDecoder::kOutputSigned16);
  if (!software_decoder_) {
    decoder_error_ = true;
    LOG(ERROR) << "Failed to initialize software decoder";
    return false;
  }
  return true;
}

void AudioDecoderSoftwareWrapper::OnDecodedBuffer(
    CastAudioDecoder::Status status,
    const media::AudioConfig& config,
    scoped_refptr<DecoderBufferBase> decoded) {
  DCHECK(delegate_);
  if (status != CastAudioDecoder::kDecodeOk) {
    delegate_->OnPushBufferComplete(MediaPipelineBackend::kBufferFailed);
    return;
  }

  if (config.samples_per_second != output_config_.samples_per_second) {
    // Sample rate of actual stream may differ from the rate reported from the
    // container format.
    output_config_.samples_per_second = config.samples_per_second;
    if (!backend_decoder_->SetConfig(output_config_)) {
      LOG(ERROR) << "Failed to set underlying backend to changed sample rate";
      delegate_->OnPushBufferComplete(MediaPipelineBackend::kBufferFailed);
      return;
    }
  }

  pending_pushed_buffer_ = decoded;
  MediaPipelineBackend::BufferStatus buffer_status =
      backend_decoder_->PushBuffer(pending_pushed_buffer_.get());
  if (buffer_status != MediaPipelineBackend::kBufferPending)
    delegate_->OnPushBufferComplete(buffer_status);
}

void AudioDecoderSoftwareWrapper::OnPushBufferComplete(
    MediaPipelineBackend::BufferStatus status) {
  DCHECK(delegate_);
  delegate_->OnPushBufferComplete(status);
}

void AudioDecoderSoftwareWrapper::OnEndOfStream() {
  DCHECK(delegate_);
  delegate_->OnEndOfStream();
}

void AudioDecoderSoftwareWrapper::OnDecoderError() {
  DCHECK(delegate_);
  delegate_->OnDecoderError();
}

void AudioDecoderSoftwareWrapper::OnKeyStatusChanged(const std::string& key_id,
                                                     CastKeyStatus key_status,
                                                     uint32_t system_code) {
  DCHECK(delegate_);
  delegate_->OnKeyStatusChanged(key_id, key_status, system_code);
}

void AudioDecoderSoftwareWrapper::OnVideoResolutionChanged(const Size& size) {
  NOTREACHED_IN_MIGRATION();
}

}  // namespace media
}  // namespace chromecast