Commit c861f7b3 authored by Jenny Wong's avatar Jenny Wong Committed by Commit Bot

[Chromecast] Let CastAudioManager provide ALSA AudioInputStreams.

CastAudioManager should only be used on platforms that use ALSA.

BUG=internal 35197382
TEST=internal CQ

Change-Id: I259dc5a5798c5a7144641a09ae6aede18125c0a6
Reviewed-on: https://chromium-review.googlesource.com/565625
Commit-Queue: Jenny Wong <jyw@chromium.org>
Reviewed-by: default avatarAlok Priyadarshi <alokp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#486872}
parent 96f33dc9
......@@ -174,6 +174,8 @@ source_set("browser") {
"//media/mojo/services",
]
}
configs += [ "//media/audio:platform_config" ]
}
# This target generates an "overlay" interface spec, allowing the Cast build to
......
......@@ -82,6 +82,10 @@
#include "chromecast/media/cdm/cast_cdm_factory.h"
#endif // BUILDFLAG(IS_CAST_USING_CMA_BACKEND)
#if defined(USE_ALSA)
#include "chromecast/media/audio/cast_audio_manager_alsa.h" // nogncheck
#endif // defined(USE_ALSA)
namespace chromecast {
namespace shell {
......@@ -202,11 +206,19 @@ CastContentBrowserClient::CreateAudioManager(
// because we already have a mixer in the audio pipeline downstream of
// CastAudioManager.
bool use_mixer = true;
#if defined(USE_ALSA)
return base::MakeUnique<media::CastAudioManagerAlsa>(
base::MakeUnique<::media::AudioThreadImpl>(), audio_log_factory,
base::MakeUnique<media::MediaPipelineBackendFactoryImpl>(
media_pipeline_backend_manager()),
GetMediaTaskRunner(), use_mixer);
#else
return base::MakeUnique<media::CastAudioManager>(
base::MakeUnique<::media::AudioThreadImpl>(), audio_log_factory,
base::MakeUnique<media::MediaPipelineBackendFactoryImpl>(
media_pipeline_backend_manager()),
GetMediaTaskRunner(), use_mixer);
#endif // defined(USE_ALSA)
}
std::unique_ptr<::media::CdmFactory>
......
......@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//chromecast/chromecast.gni")
import("//media/media_options.gni")
import("//testing/test.gni")
group("media") {
......@@ -24,6 +25,10 @@ test("cast_media_unittests") {
"test/run_all_unittests.cc",
]
if (use_alsa && !is_cast_desktop_build) {
sources += [ "audio/cast_audio_manager_alsa_unittest.cc" ]
}
deps = [
":media",
"//base",
......
......@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//media/media_options.gni")
source_set("audio") {
sources = [
"cast_audio_manager.cc",
......@@ -12,6 +14,13 @@ source_set("audio") {
"cast_audio_output_stream.h",
]
if (use_alsa) {
sources += [
"cast_audio_manager_alsa.cc",
"cast_audio_manager_alsa.h",
]
}
deps = [
"//base",
"//chromecast/base",
......@@ -22,4 +31,6 @@ source_set("audio") {
"//media",
"//media:shared_memory_support",
]
configs += [ "//media/audio:platform_config" ]
}
......@@ -24,6 +24,10 @@ const int kDefaultSampleRate = 48000;
static const int kMinimumOutputBufferSize = 512;
static const int kMaximumOutputBufferSize = 8192;
static const int kDefaultOutputBufferSize = 2048;
// TODO(jyw): Query the preferred value from media backend.
static const int kDefaultInputBufferSize = 1024;
} // namespace
namespace chromecast {
......@@ -65,10 +69,11 @@ void CastAudioManager::GetAudioInputDeviceNames(
::media::AudioParameters CastAudioManager::GetInputStreamParameters(
const std::string& device_id) {
LOG(WARNING) << "No support for input audio devices";
// Need to send a valid AudioParameters object even when it will unused.
// Need to send a valid AudioParameters object even when it will be unused.
return ::media::AudioParameters(
::media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
::media::CHANNEL_LAYOUT_STEREO, 48000, 16, 1024);
::media::CHANNEL_LAYOUT_STEREO, kDefaultSampleRate, 16,
kDefaultInputBufferSize);
}
const char* CastAudioManager::GetName() {
......
// Copyright 2017 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 "chromecast/media/audio/cast_audio_manager_alsa.h"
#include <string>
#include "base/memory/free_deleter.h"
#include "chromecast/media/cma/backend/media_pipeline_backend_factory.h"
#include "media/audio/alsa/alsa_input.h"
#include "media/audio/alsa/alsa_wrapper.h"
namespace chromecast {
namespace media {
namespace {
// TODO(alokp): Query the preferred value from media backend.
const int kDefaultSampleRate = 48000;
// TODO(jyw): Query the preferred value from media backend.
static const int kDefaultInputBufferSize = 1024;
// Since "default" and "dmix" devices are virtual devices mapped to real
// devices, we remove them from the list to avoiding duplicate counting.
static const char* kInvalidAudioInputDevices[] = {
"default", "dmix", "null",
};
} // namespace
CastAudioManagerAlsa::CastAudioManagerAlsa(
std::unique_ptr<::media::AudioThread> audio_thread,
::media::AudioLogFactory* audio_log_factory,
std::unique_ptr<MediaPipelineBackendFactory> backend_factory,
scoped_refptr<base::SingleThreadTaskRunner> backend_task_runner,
bool use_mixer)
: CastAudioManager(std::move(audio_thread),
audio_log_factory,
std::move(backend_factory),
backend_task_runner,
use_mixer),
wrapper_(new ::media::AlsaWrapper()) {}
CastAudioManagerAlsa::~CastAudioManagerAlsa() {}
bool CastAudioManagerAlsa::HasAudioInputDevices() {
return true;
}
void CastAudioManagerAlsa::ShowAudioInputSettings() {
LOG(WARNING) << "No support for input audio devices";
}
void CastAudioManagerAlsa::GetAudioInputDeviceNames(
::media::AudioDeviceNames* device_names) {
DCHECK(device_names->empty());
GetAlsaAudioDevices(kStreamCapture, device_names);
}
::media::AudioParameters CastAudioManagerAlsa::GetInputStreamParameters(
const std::string& device_id) {
// TODO(jyw): Be smarter about sample rate instead of hardcoding it.
// Need to send a valid AudioParameters object even when it will be unused.
return ::media::AudioParameters(
::media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
::media::CHANNEL_LAYOUT_STEREO, kDefaultSampleRate, 16,
kDefaultInputBufferSize);
}
::media::AudioInputStream* CastAudioManagerAlsa::MakeLinearInputStream(
const ::media::AudioParameters& params,
const std::string& device_id,
const ::media::AudioManager::LogCallback& log_callback) {
DCHECK_EQ(::media::AudioParameters::AUDIO_PCM_LINEAR, params.format());
return MakeInputStream(params, device_id);
}
::media::AudioInputStream* CastAudioManagerAlsa::MakeLowLatencyInputStream(
const ::media::AudioParameters& params,
const std::string& device_id,
const ::media::AudioManager::LogCallback& log_callback) {
DCHECK_EQ(::media::AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
return MakeInputStream(params, device_id);
}
::media::AudioInputStream* CastAudioManagerAlsa::MakeInputStream(
const ::media::AudioParameters& params,
const std::string& device_id) {
std::string device_name =
(device_id == ::media::AudioDeviceDescription::kDefaultDeviceId)
? ::media::AlsaPcmInputStream::kAutoSelectDevice
: device_id;
return new ::media::AlsaPcmInputStream(this, device_name, params,
wrapper_.get());
}
void CastAudioManagerAlsa::GetAlsaAudioDevices(
StreamType type,
::media::AudioDeviceNames* device_names) {
// Constants specified by the ALSA API for device hints.
static const char kPcmInterfaceName[] = "pcm";
int card = -1;
// Loop through the sound cards to get ALSA device hints.
while (!wrapper_->CardNext(&card) && card >= 0) {
void** hints = NULL;
int error = wrapper_->DeviceNameHint(card, kPcmInterfaceName, &hints);
if (!error) {
GetAlsaDevicesInfo(type, hints, device_names);
// Destroy the hints now that we're done with it.
wrapper_->DeviceNameFreeHint(hints);
} else {
DLOG(WARNING) << "GetAlsaAudioDevices: unable to get device hints: "
<< wrapper_->StrError(error);
}
}
}
void CastAudioManagerAlsa::GetAlsaDevicesInfo(
StreamType type,
void** hints,
::media::AudioDeviceNames* device_names) {
static const char kIoHintName[] = "IOID";
static const char kNameHintName[] = "NAME";
static const char kDescriptionHintName[] = "DESC";
const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type);
for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
// Only examine devices of the right type. Valid values are
// "Input", "Output", and NULL which means both input and output.
std::unique_ptr<char, base::FreeDeleter> io(
wrapper_->DeviceNameGetHint(*hint_iter, kIoHintName));
if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0)
continue;
// Found a device, prepend the default device since we always want
// it to be on the top of the list for all platforms. And there is
// no duplicate counting here since it is only done if the list is
// still empty. Note, pulse has exclusively opened the default
// device, so we must open the device via the "default" moniker.
if (device_names->empty())
device_names->push_front(::media::AudioDeviceName::CreateDefault());
// Get the unique device name for the device.
std::unique_ptr<char, base::FreeDeleter> unique_device_name(
wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
// Find out if the device is available.
if (IsAlsaDeviceAvailable(type, unique_device_name.get())) {
// Get the description for the device.
std::unique_ptr<char, base::FreeDeleter> desc(
wrapper_->DeviceNameGetHint(*hint_iter, kDescriptionHintName));
::media::AudioDeviceName name;
name.unique_id = unique_device_name.get();
if (desc) {
// Use the more user friendly description as name.
// Replace '\n' with '-'.
char* pret = strchr(desc.get(), '\n');
if (pret)
*pret = '-';
name.device_name = desc.get();
} else {
// Virtual devices don't necessarily have descriptions.
// Use their names instead.
name.device_name = unique_device_name.get();
}
// Store the device information.
device_names->push_back(name);
}
}
}
// static
bool CastAudioManagerAlsa::IsAlsaDeviceAvailable(StreamType type,
const char* device_name) {
if (!device_name)
return false;
// We do prefix matches on the device name to see whether to include
// it or not.
if (type == kStreamCapture) {
// Check if the device is in the list of invalid devices.
for (size_t i = 0; i < arraysize(kInvalidAudioInputDevices); ++i) {
if (strncmp(kInvalidAudioInputDevices[i], device_name,
strlen(kInvalidAudioInputDevices[i])) == 0)
return false;
}
return true;
} else {
DCHECK_EQ(kStreamPlayback, type);
// We prefer the device type that maps straight to hardware but
// goes through software conversion if needed (e.g. incompatible
// sample rate).
// TODO(joi): Should we prefer "hw" instead?
static const char kDeviceTypeDesired[] = "plughw";
return strncmp(kDeviceTypeDesired, device_name,
arraysize(kDeviceTypeDesired) - 1) == 0;
}
}
// static
const char* CastAudioManagerAlsa::UnwantedDeviceTypeWhenEnumerating(
StreamType wanted_type) {
return wanted_type == kStreamPlayback ? "Input" : "Output";
}
} // namespace media
} // namespace chromecast
// Copyright 2017 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.
#ifndef CHROMECAST_MEDIA_AUDIO_CAST_AUDIO_MANAGER_ALSA_H_
#define CHROMECAST_MEDIA_AUDIO_CAST_AUDIO_MANAGER_ALSA_H_
#include <string>
#include "base/macros.h"
#include "chromecast/media/audio/cast_audio_manager.h"
namespace media {
class AlsaWrapper;
}
namespace chromecast {
namespace media {
class CastAudioManagerAlsa : public CastAudioManager {
public:
CastAudioManagerAlsa(
std::unique_ptr<::media::AudioThread> audio_thread,
::media::AudioLogFactory* audio_log_factory,
std::unique_ptr<MediaPipelineBackendFactory> backend_factory,
scoped_refptr<base::SingleThreadTaskRunner> backend_task_runner,
bool use_mixer);
~CastAudioManagerAlsa() override;
// CastAudioManager implementation.
bool HasAudioInputDevices() override;
void ShowAudioInputSettings() override;
void GetAudioInputDeviceNames(
::media::AudioDeviceNames* device_names) override;
::media::AudioParameters GetInputStreamParameters(
const std::string& device_id) override;
private:
enum StreamType {
kStreamPlayback = 0,
kStreamCapture,
};
// CastAudioManager implementation.
::media::AudioInputStream* MakeLinearInputStream(
const ::media::AudioParameters& params,
const std::string& device_id,
const ::media::AudioManager::LogCallback& log_callback) override;
::media::AudioInputStream* MakeLowLatencyInputStream(
const ::media::AudioParameters& params,
const std::string& device_id,
const ::media::AudioManager::LogCallback& log_callback) override;
::media::AudioInputStream* MakeInputStream(
const ::media::AudioParameters& params,
const std::string& device_id);
// Gets a list of available ALSA devices.
void GetAlsaAudioDevices(StreamType type,
::media::AudioDeviceNames* device_names);
// Gets the ALSA devices' names and ids that support streams of the
// given type.
void GetAlsaDevicesInfo(StreamType type,
void** hint,
::media::AudioDeviceNames* device_names);
// Checks if the specific ALSA device is available.
static bool IsAlsaDeviceAvailable(StreamType type, const char* device_name);
static const char* UnwantedDeviceTypeWhenEnumerating(StreamType wanted_type);
std::unique_ptr<::media::AlsaWrapper> wrapper_;
DISALLOW_COPY_AND_ASSIGN(CastAudioManagerAlsa);
};
} // namespace media
} // namespace chromecast
#endif // CHROMECAST_MEDIA_AUDIO_CAST_AUDIO_MANAGER_ALSA_H_
// Copyright 2017 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 "chromecast/media/audio/cast_audio_manager_alsa.h"
#include "base/memory/ptr_util.h"
#include "base/test/test_message_loop.h"
#include "chromecast/media/cma/test/mock_media_pipeline_backend_factory.h"
#include "media/audio/fake_audio_log_factory.h"
#include "media/audio/test_audio_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::Invoke;
using testing::Return;
using testing::StrictMock;
using testing::_;
namespace chromecast {
namespace media {
namespace {
const char kDefaultAlsaDevice[] = "plug:default";
const ::media::AudioParameters kDefaultAudioParams(
::media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
::media::CHANNEL_LAYOUT_STEREO,
::media::AudioParameters::kAudioCDSampleRate,
16,
256);
void OnLogMessage(const std::string& message) {}
class CastAudioManagerAlsaTest : public testing::Test {
public:
CastAudioManagerAlsaTest() : media_thread_("CastMediaThread") {
CHECK(media_thread_.Start());
backend_factory_ = new MockMediaPipelineBackendFactory();
audio_manager_ = base::MakeUnique<CastAudioManagerAlsa>(
base::MakeUnique<::media::TestAudioThread>(), &audio_log_factory_,
base::WrapUnique(backend_factory_), media_thread_.task_runner(), false);
}
~CastAudioManagerAlsaTest() override { audio_manager_->Shutdown(); }
protected:
base::TestMessageLoop message_loop_;
base::Thread media_thread_;
::media::FakeAudioLogFactory audio_log_factory_;
std::unique_ptr<CastAudioManagerAlsa> audio_manager_;
// Owned by |audio_manager_|
MockMediaPipelineBackendFactory* backend_factory_;
};
TEST_F(CastAudioManagerAlsaTest, MakeAudioInputStream) {
::media::AudioInputStream* stream = audio_manager_->MakeAudioInputStream(
kDefaultAudioParams, kDefaultAlsaDevice, base::Bind(&OnLogMessage));
ASSERT_TRUE(stream);
EXPECT_TRUE(stream->Open());
stream->Close();
}
} // namespace
} // namespace media
} // namespace chromecast
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment