[Chromecast] Support communications device
Add 'communications' to CastAudioManagerALSA, so when user choose it as input device, will open CastAudioInputStream instead of ALSA input stream. Bug: internal: 133880006 Test: Make OTT call on Home after patching all CLs. Change-Id: I3a8609fafcf44442174778ac2c315afed52e0cb4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1660693 Commit-Queue: Kehuang Li <kehuangli@chromium.org> Reviewed-by: Kenneth MacKay <kmackay@chromium.org> Cr-Commit-Position: refs/heads/master@{#675926}
This commit is contained in:
chromecast/media/audio
@@ -4,12 +4,13 @@
|
|||||||
|
|
||||||
#include "chromecast/media/audio/cast_audio_manager_alsa.h"
|
#include "chromecast/media/audio/cast_audio_manager_alsa.h"
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "base/memory/free_deleter.h"
|
#include "base/memory/free_deleter.h"
|
||||||
#include "base/stl_util.h"
|
#include "base/stl_util.h"
|
||||||
|
#include "base/strings/string_piece.h"
|
||||||
#include "chromecast/media/audio/audio_buildflags.h"
|
#include "chromecast/media/audio/audio_buildflags.h"
|
||||||
|
#include "chromecast/media/audio/cast_audio_input_stream.h"
|
||||||
#include "chromecast/media/cma/backend/cma_backend_factory.h"
|
#include "chromecast/media/cma/backend/cma_backend_factory.h"
|
||||||
#include "media/audio/alsa/alsa_input.h"
|
#include "media/audio/alsa/alsa_input.h"
|
||||||
#include "media/audio/alsa/alsa_wrapper.h"
|
#include "media/audio/alsa/alsa_wrapper.h"
|
||||||
@@ -18,18 +19,62 @@ namespace chromecast {
|
|||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// TODO(alokp): Query the preferred value from media backend.
|
// TODO(alokp): Query the preferred value from media backend.
|
||||||
const int kDefaultSampleRate = BUILDFLAG(AUDIO_INPUT_SAMPLE_RATE);
|
const int kDefaultSampleRate = BUILDFLAG(AUDIO_INPUT_SAMPLE_RATE);
|
||||||
|
|
||||||
// TODO(jyw): Query the preferred value from media backend.
|
// TODO(jyw): Query the preferred value from media backend.
|
||||||
static const int kDefaultInputBufferSize = 1024;
|
const int kDefaultInputBufferSize = 1024;
|
||||||
|
|
||||||
|
const int kCommunicationsSampleRate = 16000;
|
||||||
|
const int kCommunicationsInputBufferSize = 160; // 10 ms.
|
||||||
|
|
||||||
// Since "default" and "dmix" devices are virtual devices mapped to real
|
// Since "default" and "dmix" devices are virtual devices mapped to real
|
||||||
// devices, we remove them from the list to avoiding duplicate counting.
|
// devices, we remove them from the list to avoiding duplicate counting.
|
||||||
static const char* kInvalidAudioInputDevices[] = {
|
constexpr base::StringPiece kInvalidAudioInputDevices[] = {
|
||||||
"default", "dmix", "null",
|
"default",
|
||||||
|
"dmix",
|
||||||
|
"null",
|
||||||
|
"communications",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Constants specified by the ALSA API for device hints.
|
||||||
|
constexpr char kPcmInterfaceName[] = "pcm";
|
||||||
|
constexpr char kIoHintName[] = "IOID";
|
||||||
|
constexpr char kNameHintName[] = "NAME";
|
||||||
|
constexpr char kDescriptionHintName[] = "DESC";
|
||||||
|
|
||||||
|
bool IsAlsaDeviceAvailable(CastAudioManagerAlsa::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 == CastAudioManagerAlsa::kStreamCapture) {
|
||||||
|
// Check if the device is in the list of invalid devices.
|
||||||
|
for (size_t i = 0; i < base::size(kInvalidAudioInputDevices); ++i) {
|
||||||
|
if (kInvalidAudioInputDevices[i] == device_name)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
DCHECK_EQ(CastAudioManagerAlsa::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?
|
||||||
|
const std::string kDeviceTypeDesired = "plughw";
|
||||||
|
return kDeviceTypeDesired == device_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string UnwantedDeviceTypeWhenEnumerating(
|
||||||
|
CastAudioManagerAlsa::StreamType wanted_type) {
|
||||||
|
return wanted_type == CastAudioManagerAlsa::kStreamPlayback ? "Input"
|
||||||
|
: "Output";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CastAudioManagerAlsa::CastAudioManagerAlsa(
|
CastAudioManagerAlsa::CastAudioManagerAlsa(
|
||||||
@@ -60,11 +105,23 @@ bool CastAudioManagerAlsa::HasAudioInputDevices() {
|
|||||||
void CastAudioManagerAlsa::GetAudioInputDeviceNames(
|
void CastAudioManagerAlsa::GetAudioInputDeviceNames(
|
||||||
::media::AudioDeviceNames* device_names) {
|
::media::AudioDeviceNames* device_names) {
|
||||||
DCHECK(device_names->empty());
|
DCHECK(device_names->empty());
|
||||||
|
// Prepend the default device since we always want it to be on the top of the
|
||||||
|
// list for all platforms. Note, pulse has exclusively opened the default
|
||||||
|
// device, so we must open the device via the "default" moniker.
|
||||||
|
device_names->push_front(::media::AudioDeviceName::CreateDefault());
|
||||||
|
device_names->push_back(::media::AudioDeviceName::CreateCommunications());
|
||||||
|
|
||||||
GetAlsaAudioDevices(kStreamCapture, device_names);
|
GetAlsaAudioDevices(kStreamCapture, device_names);
|
||||||
}
|
}
|
||||||
|
|
||||||
::media::AudioParameters CastAudioManagerAlsa::GetInputStreamParameters(
|
::media::AudioParameters CastAudioManagerAlsa::GetInputStreamParameters(
|
||||||
const std::string& device_id) {
|
const std::string& device_id) {
|
||||||
|
if (device_id == ::media::AudioDeviceDescription::kCommunicationsDeviceId) {
|
||||||
|
return ::media::AudioParameters(::media::AudioParameters::AUDIO_PCM_LINEAR,
|
||||||
|
::media::CHANNEL_LAYOUT_MONO,
|
||||||
|
kCommunicationsSampleRate,
|
||||||
|
kCommunicationsInputBufferSize);
|
||||||
|
}
|
||||||
// TODO(jyw): Be smarter about sample rate instead of hardcoding it.
|
// TODO(jyw): Be smarter about sample rate instead of hardcoding it.
|
||||||
// Need to send a valid AudioParameters object even when it will be unused.
|
// Need to send a valid AudioParameters object even when it will be unused.
|
||||||
return ::media::AudioParameters(
|
return ::media::AudioParameters(
|
||||||
@@ -96,6 +153,9 @@ void CastAudioManagerAlsa::GetAudioInputDeviceNames(
|
|||||||
(device_id == ::media::AudioDeviceDescription::kDefaultDeviceId)
|
(device_id == ::media::AudioDeviceDescription::kDefaultDeviceId)
|
||||||
? ::media::AlsaPcmInputStream::kAutoSelectDevice
|
? ::media::AlsaPcmInputStream::kAutoSelectDevice
|
||||||
: device_id;
|
: device_id;
|
||||||
|
if (device_name == ::media::AudioDeviceDescription::kCommunicationsDeviceId) {
|
||||||
|
return new CastAudioInputStream(params, device_name);
|
||||||
|
}
|
||||||
return new ::media::AlsaPcmInputStream(this, device_name, params,
|
return new ::media::AlsaPcmInputStream(this, device_name, params,
|
||||||
wrapper_.get());
|
wrapper_.get());
|
||||||
}
|
}
|
||||||
@@ -103,8 +163,6 @@ void CastAudioManagerAlsa::GetAudioInputDeviceNames(
|
|||||||
void CastAudioManagerAlsa::GetAlsaAudioDevices(
|
void CastAudioManagerAlsa::GetAlsaAudioDevices(
|
||||||
StreamType type,
|
StreamType type,
|
||||||
::media::AudioDeviceNames* device_names) {
|
::media::AudioDeviceNames* device_names) {
|
||||||
// Constants specified by the ALSA API for device hints.
|
|
||||||
static const char kPcmInterfaceName[] = "pcm";
|
|
||||||
int card = -1;
|
int card = -1;
|
||||||
|
|
||||||
// Loop through the sound cards to get ALSA device hints.
|
// Loop through the sound cards to get ALSA device hints.
|
||||||
@@ -127,28 +185,17 @@ void CastAudioManagerAlsa::GetAlsaDevicesInfo(
|
|||||||
StreamType type,
|
StreamType type,
|
||||||
void** hints,
|
void** hints,
|
||||||
::media::AudioDeviceNames* device_names) {
|
::media::AudioDeviceNames* device_names) {
|
||||||
static const char kIoHintName[] = "IOID";
|
const std::string unwanted_device_type =
|
||||||
static const char kNameHintName[] = "NAME";
|
UnwantedDeviceTypeWhenEnumerating(type);
|
||||||
static const char kDescriptionHintName[] = "DESC";
|
|
||||||
|
|
||||||
const char* unwanted_device_type = UnwantedDeviceTypeWhenEnumerating(type);
|
|
||||||
|
|
||||||
for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
|
for (void** hint_iter = hints; *hint_iter != NULL; hint_iter++) {
|
||||||
// Only examine devices of the right type. Valid values are
|
// Only examine devices of the right type. Valid values are
|
||||||
// "Input", "Output", and NULL which means both input and output.
|
// "Input", "Output", and NULL which means both input and output.
|
||||||
std::unique_ptr<char, base::FreeDeleter> io(
|
std::unique_ptr<char, base::FreeDeleter> io(
|
||||||
wrapper_->DeviceNameGetHint(*hint_iter, kIoHintName));
|
wrapper_->DeviceNameGetHint(*hint_iter, kIoHintName));
|
||||||
if (io != NULL && strcmp(unwanted_device_type, io.get()) == 0)
|
if (io && unwanted_device_type == io.get())
|
||||||
continue;
|
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.
|
// Get the unique device name for the device.
|
||||||
std::unique_ptr<char, base::FreeDeleter> unique_device_name(
|
std::unique_ptr<char, base::FreeDeleter> unique_device_name(
|
||||||
wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
|
wrapper_->DeviceNameGetHint(*hint_iter, kNameHintName));
|
||||||
@@ -162,12 +209,10 @@ void CastAudioManagerAlsa::GetAlsaDevicesInfo(
|
|||||||
::media::AudioDeviceName name;
|
::media::AudioDeviceName name;
|
||||||
name.unique_id = unique_device_name.get();
|
name.unique_id = unique_device_name.get();
|
||||||
if (desc) {
|
if (desc) {
|
||||||
|
name.device_name = desc.get();
|
||||||
// Use the more user friendly description as name.
|
// Use the more user friendly description as name.
|
||||||
// Replace '\n' with '-'.
|
// Replace '\n' with '-'.
|
||||||
char* pret = strchr(desc.get(), '\n');
|
name.device_name.replace(name.device_name.find('\n'), 1, 1, '-');
|
||||||
if (pret)
|
|
||||||
*pret = '-';
|
|
||||||
name.device_name = desc.get();
|
|
||||||
} else {
|
} else {
|
||||||
// Virtual devices don't necessarily have descriptions.
|
// Virtual devices don't necessarily have descriptions.
|
||||||
// Use their names instead.
|
// Use their names instead.
|
||||||
@@ -180,39 +225,5 @@ void CastAudioManagerAlsa::GetAlsaDevicesInfo(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 < base::size(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,
|
|
||||||
base::size(kDeviceTypeDesired) - 1) == 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
const char* CastAudioManagerAlsa::UnwantedDeviceTypeWhenEnumerating(
|
|
||||||
StreamType wanted_type) {
|
|
||||||
return wanted_type == kStreamPlayback ? "Input" : "Output";
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace chromecast
|
} // namespace chromecast
|
||||||
|
@@ -22,6 +22,11 @@ namespace media {
|
|||||||
|
|
||||||
class CastAudioManagerAlsa : public CastAudioManager {
|
class CastAudioManagerAlsa : public CastAudioManager {
|
||||||
public:
|
public:
|
||||||
|
enum StreamType {
|
||||||
|
kStreamPlayback = 0,
|
||||||
|
kStreamCapture,
|
||||||
|
};
|
||||||
|
|
||||||
CastAudioManagerAlsa(
|
CastAudioManagerAlsa(
|
||||||
std::unique_ptr<::media::AudioThread> audio_thread,
|
std::unique_ptr<::media::AudioThread> audio_thread,
|
||||||
::media::AudioLogFactory* audio_log_factory,
|
::media::AudioLogFactory* audio_log_factory,
|
||||||
@@ -41,11 +46,6 @@ class CastAudioManagerAlsa : public CastAudioManager {
|
|||||||
const std::string& device_id) override;
|
const std::string& device_id) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum StreamType {
|
|
||||||
kStreamPlayback = 0,
|
|
||||||
kStreamCapture,
|
|
||||||
};
|
|
||||||
|
|
||||||
// CastAudioManager implementation.
|
// CastAudioManager implementation.
|
||||||
::media::AudioInputStream* MakeLinearInputStream(
|
::media::AudioInputStream* MakeLinearInputStream(
|
||||||
const ::media::AudioParameters& params,
|
const ::media::AudioParameters& params,
|
||||||
@@ -70,11 +70,6 @@ class CastAudioManagerAlsa : public CastAudioManager {
|
|||||||
void** hint,
|
void** hint,
|
||||||
::media::AudioDeviceNames* device_names);
|
::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_;
|
std::unique_ptr<::media::AlsaWrapper> wrapper_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(CastAudioManagerAlsa);
|
DISALLOW_COPY_AND_ASSIGN(CastAudioManagerAlsa);
|
||||||
|
Reference in New Issue
Block a user