123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- // Copyright 2020 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 "media/audio/cras/cras_util.h"
- #include "base/logging.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/time/time.h"
- #include "media/audio/audio_device_description.h"
- #include "media/audio/cras/audio_manager_cras_base.h"
- namespace media {
- namespace {
- constexpr char kInternalInputVirtualDevice[] = "Built-in mic";
- constexpr char kInternalOutputVirtualDevice[] = "Built-in speaker";
- constexpr char kHeadphoneLineOutVirtualDevice[] = "Headphone/Line Out";
- // Names below are from the node_type_to_str function in CRAS server.
- // https://chromium.googlesource.com/chromiumos/third_party/adhd/+/refs/heads/main/cras/src/server/cras_iodev_list.c
- constexpr char kInternalSpeaker[] = "INTERNAL_SPEAKER";
- constexpr char kHeadphone[] = "HEADPHONE";
- constexpr char kHDMI[] = "HDMI";
- constexpr char kLineout[] = "LINEOUT";
- constexpr char kMic[] = "MIC";
- constexpr char kInternalMic[] = "INTERNAL_MIC";
- constexpr char kFrontMic[] = "FRONT_MIC";
- constexpr char kRearMic[] = "REAR_MIC";
- constexpr char kBluetoothNBMic[] = "BLUETOOTH_NB_MIC";
- constexpr char kUSB[] = "USB";
- constexpr char kBluetooth[] = "BLUETOOTH";
- constexpr char kAlsaLoopback[] = "ALSA_LOOPBACK";
- // Returns if that an input or output audio device is for simple usage like
- // playback or recording for user. In contrast, audio device such as loopback,
- // always on keyword recognition (HOTWORD), and keyboard mic are not for simple
- // usage.
- // One special case is ALSA loopback device, which will only exist under
- // testing. We want it visible to users for e2e tests.
- bool IsForSimpleUsage(std::string type) {
- return type == kInternalSpeaker || type == kHeadphone || type == kHDMI ||
- type == kLineout || type == kMic || type == kInternalMic ||
- type == kFrontMic || type == kRearMic || type == kBluetoothNBMic ||
- type == kUSB || type == kBluetooth || type == kAlsaLoopback;
- }
- bool IsInternalMic(std::string type) {
- return type == kInternalMic || type == kFrontMic || type == kRearMic;
- }
- // Connects to the CRAS server.
- libcras_client* CrasConnect() {
- libcras_client* client;
- client = libcras_client_create();
- if (!client) {
- LOG(ERROR) << "Couldn't create CRAS client.\n";
- return nullptr;
- }
- if (libcras_client_connect(client)) {
- LOG(ERROR) << "Couldn't connect CRAS client.\n";
- libcras_client_destroy(client);
- return nullptr;
- }
- return client;
- }
- // Disconnects from the CRAS server.
- void CrasDisconnect(libcras_client** client) {
- if (*client) {
- libcras_client_stop(*client);
- libcras_client_destroy(*client);
- *client = nullptr;
- }
- }
- } // namespace
- CrasDevice::CrasDevice() = default;
- CrasDevice::CrasDevice(struct libcras_node_info* node, DeviceType type)
- : type(type) {
- int rc;
- rc = libcras_node_info_get_id(node, &id);
- if (rc) {
- LOG(ERROR) << "Failed to get the node id: " << rc;
- id = 0;
- }
- rc = libcras_node_info_get_dev_idx(node, &dev_idx);
- if (rc) {
- LOG(ERROR) << "Failed to get the dev idx: " << rc;
- dev_idx = 0;
- }
- rc = libcras_node_info_is_plugged(node, &plugged);
- if (rc) {
- LOG(ERROR) << "Failed to get if the node is plugged: " << rc;
- plugged = false;
- }
- rc = libcras_node_info_is_active(node, &active);
- if (rc) {
- LOG(ERROR) << "Failed to get if the node is active: " << rc;
- active = false;
- }
- char* type_str;
- rc = libcras_node_info_get_type(node, &type_str);
- if (rc) {
- LOG(ERROR) << "Failed to get the node type: " << rc;
- node_type = nullptr;
- }
- node_type = type_str;
- char* node_name;
- rc = libcras_node_info_get_node_name(node, &node_name);
- if (rc) {
- LOG(ERROR) << "Failed to get the node name: " << rc;
- node_name = nullptr;
- }
- char* device_name;
- rc = libcras_node_info_get_dev_name(node, &device_name);
- if (rc) {
- LOG(ERROR) << "Failed to get the dev name: " << rc;
- device_name = nullptr;
- }
- rc = libcras_node_info_get_max_supported_channels(node,
- &max_supported_channels);
- if (rc) {
- LOG(ERROR) << "Failed to get max supported channels: " << rc;
- max_supported_channels = 0;
- }
- name = std::string(node_name);
- if (name.empty() || name == "(default)")
- name = device_name;
- dev_name = device_name;
- }
- void mergeDevices(CrasDevice& old_dev, CrasDevice& new_dev) {
- if (old_dev.node_type == kLineout || new_dev.node_type == kLineout) {
- old_dev.name = kHeadphoneLineOutVirtualDevice;
- old_dev.node_type = "";
- } else if (old_dev.node_type == kInternalSpeaker ||
- new_dev.node_type == kInternalSpeaker) {
- old_dev.name = kInternalOutputVirtualDevice;
- old_dev.node_type = "";
- } else if (IsInternalMic(old_dev.node_type) ||
- IsInternalMic(new_dev.node_type)) {
- old_dev.name = kInternalInputVirtualDevice;
- old_dev.node_type = "";
- } else {
- LOG(WARNING) << "Failed to create virtual device for " << old_dev.name;
- }
- old_dev.active |= new_dev.active;
- }
- CrasUtil::CrasUtil() = default;
- CrasUtil::~CrasUtil() = default;
- std::vector<CrasDevice> CrasUtil::CrasGetAudioDevices(DeviceType type) {
- std::vector<CrasDevice> devices;
- libcras_client* client = CrasConnect();
- if (!client)
- return devices;
- int rc;
- struct libcras_node_info** nodes;
- size_t num_nodes;
- if (type == DeviceType::kInput) {
- rc =
- libcras_client_get_nodes(client, CRAS_STREAM_INPUT, &nodes, &num_nodes);
- } else {
- rc = libcras_client_get_nodes(client, CRAS_STREAM_OUTPUT, &nodes,
- &num_nodes);
- }
- if (rc < 0) {
- LOG(ERROR) << "Failed to get devices: " << std::strerror(rc);
- CrasDisconnect(&client);
- return devices;
- }
- for (size_t i = 0; i < num_nodes; i++) {
- auto new_dev = CrasDevice(nodes[i], type);
- if (!new_dev.plugged || !IsForSimpleUsage(new_dev.node_type))
- continue;
- bool added = false;
- for (auto& dev : devices) {
- if (dev.dev_idx == new_dev.dev_idx) {
- mergeDevices(dev, new_dev);
- added = true;
- break;
- }
- }
- if (!added)
- devices.emplace_back(new_dev);
- }
- libcras_node_info_array_destroy(nodes, num_nodes);
- CrasDisconnect(&client);
- return devices;
- }
- int CrasUtil::CrasGetAecSupported() {
- libcras_client* client = CrasConnect();
- if (!client)
- return 0;
- int supported;
- libcras_client_get_aec_supported(client, &supported);
- CrasDisconnect(&client);
- return supported;
- }
- int CrasUtil::CrasGetAecGroupId() {
- libcras_client* client = CrasConnect();
- if (!client)
- return -1;
- int id;
- int rc = libcras_client_get_aec_group_id(client, &id);
- CrasDisconnect(&client);
- return rc < 0 ? rc : id;
- }
- int CrasUtil::CrasGetDefaultOutputBufferSize() {
- libcras_client* client = CrasConnect();
- if (!client)
- return -1;
- int size;
- int rc = libcras_client_get_default_output_buffer_size(client, &size);
- CrasDisconnect(&client);
- return rc < 0 ? rc : size;
- }
- } // namespace media
|