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

#include <utility>

#include "base/logging.h"
#include "base/task/single_thread_task_runner.h"
#include "chromecast/media/audio/audio_buildflags.h"
#include "chromecast/media/audio/cast_audio_input_stream.h"
#include "chromecast/media/audio/cast_audio_output_stream.h"
#include "chromecast/media/audio/cast_audio_output_utils.h"
#include "media/audio/android/audio_track_output_stream.h"
#include "media/audio/audio_device_name.h"
#include "media/base/audio_parameters.h"
#include "media/base/channel_layout.h"

namespace chromecast {
namespace media {
namespace {

const int kDefaultSampleRate = 48000;
const int kDefaultInputBufferSize = 1024;

#if BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)
const int kCommunicationsSampleRate = 16000;
const int kCommunicationsInputBufferSize = 160;  // 10 ms.
#endif  // BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)

bool ShouldUseCastAudioOutputStream(bool is_audio_app,
                                    bool is_group,
                                    const ::media::AudioParameters& params) {
  return is_audio_app || is_group ||
         (params.effects() & ::media::AudioParameters::AUDIO_PREFETCH);
}

}  // namespace

CastAudioManagerAndroid::CastAudioManagerAndroid(
    std::unique_ptr<::media::AudioThread> audio_thread,
    ::media::AudioLogFactory* audio_log_factory,
    CastAudioManagerHelper::Delegate* delegate,
    base::RepeatingCallback<CmaBackendFactory*()> backend_factory_getter,
    scoped_refptr<base::SingleThreadTaskRunner> media_task_runner)
    : ::media::AudioManagerAndroid(std::move(audio_thread), audio_log_factory),
      helper_(this,
              delegate,
              std::move(backend_factory_getter),
              std::move(media_task_runner)) {}

CastAudioManagerAndroid::~CastAudioManagerAndroid() = default;

bool CastAudioManagerAndroid::HasAudioInputDevices() {
#if BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)
  return true;
#else
  return false;
#endif  // BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)
}

void CastAudioManagerAndroid::GetAudioInputDeviceNames(
    ::media::AudioDeviceNames* device_names) {
  DCHECK(device_names->empty());
#if BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)
  device_names->push_back(::media::AudioDeviceName::CreateCommunications());
#else
  LOG(WARNING) << "No support for input audio devices";
#endif  // BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)
}

::media::AudioParameters CastAudioManagerAndroid::GetInputStreamParameters(
    const std::string& device_id) {
#if BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)
  if (device_id == ::media::AudioDeviceDescription::kCommunicationsDeviceId) {
    return ::media::AudioParameters(::media::AudioParameters::AUDIO_PCM_LINEAR,
                                    ::media::CHANNEL_LAYOUT_MONO,
                                    kCommunicationsSampleRate,
                                    kCommunicationsInputBufferSize);
  }
#endif  // BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)
  LOG(WARNING) << "No support for input audio devices";
  // Need to send a valid AudioParameters object even when it will be unused.
  return ::media::AudioParameters(
      ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
      ::media::ChannelLayoutConfig::Stereo(), kDefaultSampleRate,
      kDefaultInputBufferSize);
}

::media::AudioInputStream* CastAudioManagerAndroid::MakeLinearInputStream(
    const ::media::AudioParameters& params,
    const std::string& device_id,
    const ::media::AudioManager::LogCallback& log_callback) {
#if BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)
  if (device_id == ::media::AudioDeviceDescription::kCommunicationsDeviceId) {
    return new CastAudioInputStream(this, params, device_id);
  }
#endif  // BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)
  LOG(WARNING) << "No support for input audio devices";
  return nullptr;
}

::media::AudioInputStream* CastAudioManagerAndroid::MakeLowLatencyInputStream(
    const ::media::AudioParameters& params,
    const std::string& device_id,
    const ::media::AudioManager::LogCallback& log_callback) {
#if BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)
  if (device_id == ::media::AudioDeviceDescription::kCommunicationsDeviceId) {
    return new CastAudioInputStream(this, params, device_id);
  }
#endif  // BUILDFLAG(ENABLE_AUDIO_CAPTURE_SERVICE)
  LOG(WARNING) << "No support for input audio devices";
  return nullptr;
}

void CastAudioManagerAndroid::GetAudioOutputDeviceNames(
    ::media::AudioDeviceNames* device_names) {
  DCHECK(device_names->empty());
  DCHECK(HasAudioOutputDevices());

  // Default device name is added inside AudioManagerAndroid.
  ::media::AudioManagerAndroid::GetAudioOutputDeviceNames(device_names);

  device_names->push_back(::media::AudioDeviceName::CreateCommunications());
}

::media::AudioOutputStream* CastAudioManagerAndroid::MakeLinearOutputStream(
    const ::media::AudioParameters& params,
    const ::media::AudioManager::LogCallback& log_callback) {
  DCHECK_EQ(::media::AudioParameters::AUDIO_PCM_LINEAR, params.format());
  // MakeLinearOutputStream is only used on Windows. In this case, we cannot get
  // a valid session id based on kDefaultDeviceId. Therefore we cannot know
  // whether it is an audio only session.
  if (ShouldUseCastAudioOutputStream(false /* is_audio_app */,
                                     false /* is_group */, params)) {
    LOG(WARNING) << __func__ << ": Cannot get valid session_id.";
    return new CastAudioOutputStream(
        &helper_, params, ::media::AudioDeviceDescription::kDefaultDeviceId,
        false /* use_mixer_service */);
  }

  return ::media::AudioManagerAndroid::MakeLinearOutputStream(params,
                                                              log_callback);
}

::media::AudioOutputStream* CastAudioManagerAndroid::MakeLowLatencyOutputStream(
    const ::media::AudioParameters& params,
    const std::string& device_id_or_group_id,
    const ::media::AudioManager::LogCallback& log_callback) {
  DCHECK_EQ(::media::AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
  std::string session_id =
      helper_.GetSessionId(GetGroupId(device_id_or_group_id));
  bool is_audio_app = helper_.IsAudioOnlySession(session_id);
  bool is_group = helper_.IsGroup(session_id);
  if (ShouldUseCastAudioOutputStream(is_audio_app, is_group, params)) {
    return new CastAudioOutputStream(
        &helper_, params,
        device_id_or_group_id.empty()
            ? ::media::AudioDeviceDescription::kDefaultDeviceId
            : device_id_or_group_id,
        false /* use_mixer_service */);
  }

  return ::media::AudioManagerAndroid::MakeLowLatencyOutputStream(
      params, device_id_or_group_id, log_callback);
}

::media::AudioOutputStream* CastAudioManagerAndroid::MakeBitstreamOutputStream(
    const ::media::AudioParameters& params,
    const std::string& device_id,
    const ::media::AudioManager::LogCallback& log_callback) {
  DCHECK(params.IsBitstreamFormat());
  return new ::media::AudioTrackOutputStream(this, params);
}

::media::AudioOutputStream* CastAudioManagerAndroid::MakeAudioOutputStreamProxy(
    const ::media::AudioParameters& params,
    const std::string& device_id) {
  std::string session_id = helper_.GetSessionId(GetGroupId(device_id));
  bool is_audio_app = helper_.IsAudioOnlySession(session_id);
  bool is_group = helper_.IsGroup(session_id);
  if (ShouldUseCastAudioOutputStream(is_audio_app, is_group, params)) {
    // Override to use MakeAudioOutputStream to prevent the audio output stream
    // from closing during pause/stop.
    return MakeAudioOutputStream(params, device_id,
                                 /*log_callback, not used*/ base::DoNothing());
  }

  return ::media::AudioManagerAndroid::MakeAudioOutputStreamProxy(params,
                                                                  device_id);
}

}  // namespace media
}  // namespace chromecast