chromium/chromecast/media/audio/cma_audio_output.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/audio/cma_audio_output.h"

#include <algorithm>
#include <limits>
#include <utility>

#include "base/logging.h"
#include "chromecast/base/task_runner_impl.h"
#include "chromecast/media/api/cma_backend_factory.h"
#include "chromecast/media/base/cast_decoder_buffer_impl.h"
#include "chromecast/media/cma/base/decoder_config_adapter.h"
#include "chromecast/public/volume_control.h"
#include "media/audio/audio_device_description.h"

namespace chromecast {
namespace media {

namespace {

AudioContentType GetContentType(const std::string& device_id) {
  if (::media::AudioDeviceDescription::IsCommunicationsDevice(device_id)) {
    return AudioContentType::kCommunication;
  }
  return AudioContentType::kMedia;
}

int GetSampleSize(SampleFormat sample_format) {
  switch (sample_format) {
    case kUnknownSampleFormat:
      return 0;
    case kSampleFormatU8:
    case kSampleFormatPlanarU8:
      return sizeof(uint8_t);
    case kSampleFormatS16:
    case kSampleFormatPlanarS16:
      return sizeof(int16_t);
    case kSampleFormatS32:
    case kSampleFormatPlanarS32:
      return sizeof(int32_t);
    case kSampleFormatF32:
    case kSampleFormatPlanarF32:
      return sizeof(float);
    case kSampleFormatS24:
      return 3;
  }
}

}  // namespace

CmaAudioOutput::CmaAudioOutput(
    const ::media::AudioParameters& audio_params,
    SampleFormat sample_format,
    const std::string& device_id,
    const std::string& application_session_id,
    MediaPipelineDeviceParams::MediaSyncType sync_type,
    bool use_hw_av_sync,
    int audio_track_session_id,
    CmaBackendFactory* cma_backend_factory,
    CmaBackend::Decoder::Delegate* delegate)
    : audio_params_(audio_params),
      sample_size_(GetSampleSize(sample_format)),
      use_hw_av_sync_(use_hw_av_sync),
      delegate_(delegate),
      timestamp_helper_(audio_params_.sample_rate()) {
  DCHECK(delegate_);
  Initialize(sample_format, device_id, application_session_id, sync_type,
             audio_track_session_id, cma_backend_factory);
}

CmaAudioOutput::~CmaAudioOutput() = default;

void CmaAudioOutput::Initialize(
    SampleFormat sample_format,
    const std::string& device_id,
    const std::string& application_session_id,
    MediaPipelineDeviceParams::MediaSyncType sync_type,
    int audio_track_session_id,
    CmaBackendFactory* cma_backend_factory) {
  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
  DCHECK(cma_backend_factory);

  auto cma_backend_task_runner = std::make_unique<TaskRunnerImpl>();
  MediaPipelineDeviceParams device_params(
      sync_type, MediaPipelineDeviceParams::kAudioStreamNormal,
      cma_backend_task_runner.get(), GetContentType(device_id), device_id);
  device_params.session_id = application_session_id;
  auto cma_backend = cma_backend_factory->CreateBackend(device_params);
  if (!cma_backend) {
    return;
  }

  auto* audio_decoder = cma_backend->CreateAudioDecoder();
  if (!audio_decoder) {
    return;
  }
  audio_decoder->SetDelegate(delegate_);

  AudioConfig audio_config;
  audio_config.codec = kCodecPCM;
  audio_config.channel_layout =
      DecoderConfigAdapter::ToChannelLayout(audio_params_.channel_layout());
  audio_config.sample_format = sample_format;
  audio_config.bytes_per_channel = sample_size_;
  audio_config.channel_number = audio_params_.channels();
  audio_config.samples_per_second = audio_params_.sample_rate();
  audio_config.use_hw_av_sync = use_hw_av_sync_;
  audio_config.audio_track_session_id = audio_track_session_id;
  DCHECK(IsValidConfig(audio_config));
  // Need to first set the config of the audio decoder then initialize the cma
  // backend if succeed.
  if (!audio_decoder->SetConfig(audio_config) || !cma_backend->Initialize()) {
    return;
  }

  cma_backend_task_runner_ = std::move(cma_backend_task_runner);
  cma_backend_ = std::move(cma_backend);
  audio_decoder_ = audio_decoder;

  timestamp_helper_.SetBaseTimestamp(base::TimeDelta());
}

bool CmaAudioOutput::Start(int64_t start_pts) {
  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
  if (!cma_backend_ || !cma_backend_->Start(start_pts)) {
    return false;
  }
  return true;
}

void CmaAudioOutput::Stop() {
  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
  if (cma_backend_) {
    cma_backend_->Stop();
  }
}

bool CmaAudioOutput::Pause() {
  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
  if (!cma_backend_ || !cma_backend_->Pause()) {
    return false;
  }
  return true;
}

bool CmaAudioOutput::Resume() {
  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
  if (!cma_backend_ || !cma_backend_->Resume()) {
    return false;
  }
  return true;
}

bool CmaAudioOutput::SetVolume(double volume) {
  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
  if (!audio_decoder_) {
    return false;
  }
  return audio_decoder_->SetVolume(volume);
}

void CmaAudioOutput::PushBuffer(
    scoped_refptr<CastDecoderBufferImpl> decoder_buffer,
    bool is_silence) {
  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
  DCHECK_GT(decoder_buffer->data_size(), 0u);
  DCHECK_EQ(
      decoder_buffer->data_size() % (sample_size_ * audio_params_.channels()),
      0u);
  DCHECK(audio_decoder_);

  if (!is_silence) {
    if (!use_hw_av_sync_) {
      // Keep the timestamp of the buffer if the stream is on hardware av sync
      // mode.
      decoder_buffer->set_timestamp(timestamp_helper_.GetTimestamp());
    }
    int frame_count =
        decoder_buffer->data_size() / (sample_size_ * audio_params_.channels());
    timestamp_helper_.AddFrames(frame_count);
  }
  CmaBackend::BufferStatus status =
      audio_decoder_->PushBuffer(std::move(decoder_buffer));
  if (status != CmaBackend::BufferStatus::kBufferPending)
    delegate_->OnPushBufferComplete(status);
}

CmaBackend::AudioDecoder::RenderingDelay CmaAudioOutput::GetRenderingDelay() {
  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
  DCHECK(audio_decoder_);
  return audio_decoder_->GetRenderingDelay();
}

CmaBackend::AudioDecoder::AudioTrackTimestamp
CmaAudioOutput::GetAudioTrackTimestamp() {
  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
  DCHECK(audio_decoder_);
  return audio_decoder_->GetAudioTrackTimestamp();
}

int64_t CmaAudioOutput::GetTotalFrames() {
  DCHECK_CALLED_ON_VALID_THREAD(media_thread_checker_);
  return timestamp_helper_.frame_count();
}

}  // namespace media
}  // namespace chromecast