blob: 7d33c35d57ab3a1c1a6843534614e0ee80f1d39e [file] [log] [blame]
// Copyright 2018 The Chromium OS 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_perception/chrome_audio_service_client_impl.h"
#include <utility>
#include "base/logging.h"
#include "media_perception/device_management.pb.h"
#include "media_perception/proto_mojom_conversion.h"
#include "media_perception/serialized_proto.h"
namespace mri {
namespace {
// Max number of devices to probe.
const size_t MAX_IODEVS = 10;
// Max number of nodes to probe.
const size_t MAX_IONODES = 20;
} // namespace
bool ChromeAudioServiceClientImpl::Connect() {
if (IsConnected()) {
LOG(INFO) << "Client is already connected.";
return true;
}
base::AutoLock auto_lock(client_lock_);
// cras_helper_create_connect returns 0 on success, or a negative error code
// on failure.
int rc = cras_helper_create_connect(&client_);
if (rc < 0) {
LOG(ERROR) << "Failed to connect to CrAS server with error code: " << rc;
DestroyClient();
return false;
}
return true;
}
void ChromeAudioServiceClientImpl::DestroyClient() {
base::AutoLock auto_lock(client_lock_);
if (client_) {
cras_client_destroy(client_);
client_ = nullptr;
}
}
ChromeAudioServiceClientImpl::~ChromeAudioServiceClientImpl() {
DestroyClient();
}
bool ChromeAudioServiceClientImpl::IsConnected() {
base::AutoLock auto_lock(client_lock_);
return client_ != nullptr;
}
std::vector<SerializedAudioDevice>
ChromeAudioServiceClientImpl::GetInputDevices() {
std::vector<SerializedAudioDevice> serialized_audio_devices;
if (!IsConnected()) {
LOG(WARNING) << "CrAS client is not connected. "
<< "Must Connect() to query devices.";
return serialized_audio_devices;
}
struct cras_iodev_info devs[MAX_IODEVS];
struct cras_ionode_info nodes[MAX_IONODES];
size_t num_devs = MAX_IODEVS;
size_t num_nodes = MAX_IONODES;
base::AutoLock auto_lock(client_lock_);
// Get the current list of input devices. Return 0 on success, -EINVAL if the
// client isn't valid or isn't running.
int rc = cras_client_get_input_devices(client_, devs, nodes, &num_devs,
&num_nodes);
if (rc != 0) {
LOG(ERROR) << "Failed to query audio input devices with error code: " << rc;
return serialized_audio_devices;
}
LOG(INFO) << "Got devices from CrAS.";
// Loop through the result and create the necessary AudioDevice protos.
for (int i = 0; i < num_devs; i++) {
if (std::string(devs[i].name).empty()) {
continue;
}
AudioDevice audio_device;
audio_device.set_id(std::to_string(devs[i].stable_id));
audio_device.set_display_name(std::string(devs[i].name));
serialized_audio_devices.push_back(
Serialized<AudioDevice>(audio_device).GetBytes());
// Add the stable device id to the audio receivers map if it does not exist.
if (device_id_to_audio_receiver_map_.find(audio_device.id()) ==
device_id_to_audio_receiver_map_.end()) {
device_id_to_audio_receiver_map_.emplace(
audio_device.id(), AudioReceiver(audio_device.id()));
}
}
return serialized_audio_devices;
}
bool ChromeAudioServiceClientImpl::IsAudioCaptureStartedForDevice(
const std::string& device_id, SerializedAudioStreamParams* capture_format) {
base::AutoLock auto_lock(client_lock_);
std::map<std::string, AudioReceiver>::iterator it =
device_id_to_audio_receiver_map_.find(device_id);
if (it == device_id_to_audio_receiver_map_.end()) {
LOG(WARNING) << "Device id not found in map.";
return false;
}
if (it->second.GetFrameHandlerCount() == 0) {
return false;
}
*capture_format =
Serialized<AudioStreamParams>(it->second.GetAudioStreamParams())
.GetBytes();
return true;
}
int ChromeAudioServiceClientImpl::AddFrameHandler(
const std::string& device_id,
const SerializedAudioStreamParams& capture_format,
AudioFrameHandler handler) {
AudioStreamParams params =
Serialized<AudioStreamParams>(capture_format).Deserialize();
base::AutoLock auto_lock(client_lock_);
std::map<std::string, AudioReceiver>::iterator receiver_it =
device_id_to_audio_receiver_map_.find(device_id);
if (receiver_it == device_id_to_audio_receiver_map_.end()) {
LOG(WARNING) << "Device id not found in map.";
return 0;
}
// If the internal handler map already has a frame handler, then we need to
// compare the requested params with the params saved for the device.
if (receiver_it->second.GetFrameHandlerCount() > 0 &&
!receiver_it->second.CaptureFormatMatches(params)) {
LOG(WARNING)
<< "Capture formats do not match for device already streaming.";
return 0;
} else if (receiver_it->second.GetFrameHandlerCount() == 0) {
// Start streaming audio for this device since it not started.
struct cras_iodev_info devs[MAX_IODEVS];
struct cras_ionode_info nodes[MAX_IONODES];
size_t num_devs = MAX_IODEVS;
size_t num_nodes = MAX_IONODES;
// Get the current list of input devices. Return 0 on success, -EINVAL if
// the client isn't valid or isn't running.
int rc = cras_client_get_input_devices(client_, devs, nodes, &num_devs,
&num_nodes);
if (rc != 0) {
LOG(ERROR) << "Failed to query audio input devices with error code: "
<< rc;
return 0;
}
int dev_idx = NO_DEVICE;
bool found_matched_device = false;
for (int i = 0; i < num_devs; i++) {
if (std::to_string(devs[i].stable_id) == device_id) {
LOG(INFO) << "Found audio device with stable id: " << device_id;
found_matched_device = true;
dev_idx = devs[i].idx;
}
}
if (!found_matched_device) {
LOG(WARNING) << "Failed to find a matched audio device for id: "
<< device_id;
return 0;
}
if (!receiver_it->second.SetAudioStreamParams(params)) {
LOG(ERROR) << "Failed to set the audio stream params.";
return 0;
}
// Use the dev_idx to start audio capture.
if (!receiver_it->second.StartAudioCaptureForDeviceIdx(client_, dev_idx)) {
LOG(ERROR) << "Failed to start audio capture.";
return 0;
}
}
// Store the audio stream parameters for this device.
return receiver_it->second.AddAudioFrameHandler(std::move(handler));
}
bool ChromeAudioServiceClientImpl::RemoveFrameHandler(
const std::string& device_id, int frame_handler_id) {
base::AutoLock auto_lock(client_lock_);
std::map<std::string, AudioReceiver>::iterator it =
device_id_to_audio_receiver_map_.find(device_id);
if (it == device_id_to_audio_receiver_map_.end()) {
LOG(WARNING) << "Device id not found in map.";
return false;
}
bool success = it->second.RemoveFrameHandler(frame_handler_id);
if (success && it->second.GetFrameHandlerCount() == 0) {
it->second.StopAudioCapture(client_);
}
return success;
}
} // namespace mri